xref: /linux/sound/pci/ice1712/wtm.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f6cdab5fSClement Guedez /*
3f6cdab5fSClement Guedez  *	ALSA driver for ICEnsemble VT1724 (Envy24HT)
4f6cdab5fSClement Guedez  *
5f6cdab5fSClement Guedez  *	Lowlevel functions for Ego Sys Waveterminal 192M
6f6cdab5fSClement Guedez  *
7f6cdab5fSClement Guedez  *		Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
8f6cdab5fSClement Guedez  *		Some functions are taken from the Prodigy192 driver
9f6cdab5fSClement Guedez  *		source
10f6cdab5fSClement Guedez  */
11f6cdab5fSClement Guedez 
12f6cdab5fSClement Guedez 
13f6cdab5fSClement Guedez 
14f6cdab5fSClement Guedez #include <linux/delay.h>
15f6cdab5fSClement Guedez #include <linux/interrupt.h>
16f6cdab5fSClement Guedez #include <linux/init.h>
17f6cdab5fSClement Guedez #include <sound/core.h>
1816ddbe73SClément Guedez #include <sound/tlv.h>
191aa9a4eaSClément Guedez #include <linux/slab.h>
20f6cdab5fSClement Guedez 
21f6cdab5fSClement Guedez #include "ice1712.h"
22f6cdab5fSClement Guedez #include "envy24ht.h"
23f6cdab5fSClement Guedez #include "wtm.h"
24f6cdab5fSClement Guedez #include "stac946x.h"
25f6cdab5fSClement Guedez 
261aa9a4eaSClément Guedez struct wtm_spec {
271aa9a4eaSClément Guedez 	/* rate change needs atomic mute/unmute of all dacs*/
281aa9a4eaSClément Guedez 	struct mutex mute_mutex;
291aa9a4eaSClément Guedez };
301aa9a4eaSClément Guedez 
31f6cdab5fSClement Guedez 
32f6cdab5fSClement Guedez /*
33f6cdab5fSClement Guedez  *	2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
34f6cdab5fSClement Guedez  */
stac9460_put(struct snd_ice1712 * ice,int reg,unsigned char val)35f6cdab5fSClement Guedez static inline void stac9460_put(struct snd_ice1712 *ice, int reg,
36f6cdab5fSClement Guedez 						unsigned char val)
37f6cdab5fSClement Guedez {
38f6cdab5fSClement Guedez 	snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val);
39f6cdab5fSClement Guedez }
40f6cdab5fSClement Guedez 
stac9460_get(struct snd_ice1712 * ice,int reg)41f6cdab5fSClement Guedez static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
42f6cdab5fSClement Guedez {
43f6cdab5fSClement Guedez 	return snd_vt1724_read_i2c(ice, STAC9460_I2C_ADDR, reg);
44f6cdab5fSClement Guedez }
45f6cdab5fSClement Guedez 
46f6cdab5fSClement Guedez /*
47f6cdab5fSClement Guedez  *	2*ADC 2*DAC no2 ringbuffer r/w on i2c bus
48f6cdab5fSClement Guedez  */
stac9460_2_put(struct snd_ice1712 * ice,int reg,unsigned char val)49f6cdab5fSClement Guedez static inline void stac9460_2_put(struct snd_ice1712 *ice, int reg,
50f6cdab5fSClement Guedez 						unsigned char val)
51f6cdab5fSClement Guedez {
52f6cdab5fSClement Guedez 	snd_vt1724_write_i2c(ice, STAC9460_2_I2C_ADDR, reg, val);
53f6cdab5fSClement Guedez }
54f6cdab5fSClement Guedez 
stac9460_2_get(struct snd_ice1712 * ice,int reg)55f6cdab5fSClement Guedez static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
56f6cdab5fSClement Guedez {
57f6cdab5fSClement Guedez 	return snd_vt1724_read_i2c(ice, STAC9460_2_I2C_ADDR, reg);
58f6cdab5fSClement Guedez }
59f6cdab5fSClement Guedez 
60f6cdab5fSClement Guedez 
61f6cdab5fSClement Guedez /*
62f6cdab5fSClement Guedez  *	DAC mute control
63f6cdab5fSClement Guedez  */
stac9460_dac_mute_all(struct snd_ice1712 * ice,unsigned char mute,unsigned short int * change_mask)641aa9a4eaSClément Guedez static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute,
651aa9a4eaSClément Guedez 				unsigned short int *change_mask)
661aa9a4eaSClément Guedez {
671aa9a4eaSClément Guedez 	unsigned char new, old;
681aa9a4eaSClément Guedez 	int id, idx, change;
691aa9a4eaSClément Guedez 
701aa9a4eaSClément Guedez 	/*stac9460 1*/
711aa9a4eaSClément Guedez 	for (id = 0; id < 7; id++) {
721aa9a4eaSClément Guedez 		if (*change_mask & (0x01 << id)) {
731aa9a4eaSClément Guedez 			if (id == 0)
741aa9a4eaSClément Guedez 				idx = STAC946X_MASTER_VOLUME;
751aa9a4eaSClément Guedez 			else
761aa9a4eaSClément Guedez 				idx = STAC946X_LF_VOLUME - 1 + id;
771aa9a4eaSClément Guedez 			old = stac9460_get(ice, idx);
781aa9a4eaSClément Guedez 			new = (~mute << 7 & 0x80) | (old & ~0x80);
791aa9a4eaSClément Guedez 			change = (new != old);
801aa9a4eaSClément Guedez 			if (change) {
811aa9a4eaSClément Guedez 				stac9460_put(ice, idx, new);
821aa9a4eaSClément Guedez 				*change_mask = *change_mask | (0x01 << id);
831aa9a4eaSClément Guedez 			} else {
841aa9a4eaSClément Guedez 				*change_mask = *change_mask & ~(0x01 << id);
851aa9a4eaSClément Guedez 			}
861aa9a4eaSClément Guedez 		}
871aa9a4eaSClément Guedez 	}
881aa9a4eaSClément Guedez 
891aa9a4eaSClément Guedez 	/*stac9460 2*/
901aa9a4eaSClément Guedez 	for (id = 0; id < 3; id++) {
911aa9a4eaSClément Guedez 		if (*change_mask & (0x01 << (id + 7))) {
921aa9a4eaSClément Guedez 			if (id == 0)
931aa9a4eaSClément Guedez 				idx = STAC946X_MASTER_VOLUME;
941aa9a4eaSClément Guedez 			else
951aa9a4eaSClément Guedez 				idx = STAC946X_LF_VOLUME - 1 + id;
961aa9a4eaSClément Guedez 			old = stac9460_2_get(ice, idx);
971aa9a4eaSClément Guedez 			new = (~mute << 7 & 0x80) | (old & ~0x80);
981aa9a4eaSClément Guedez 			change = (new != old);
991aa9a4eaSClément Guedez 			if (change) {
1001aa9a4eaSClément Guedez 				stac9460_2_put(ice, idx, new);
1011aa9a4eaSClément Guedez 				*change_mask = *change_mask | (0x01 << id);
1021aa9a4eaSClément Guedez 			} else {
1031aa9a4eaSClément Guedez 				*change_mask = *change_mask & ~(0x01 << id);
1041aa9a4eaSClément Guedez 			}
1051aa9a4eaSClément Guedez 		}
1061aa9a4eaSClément Guedez 	}
1071aa9a4eaSClément Guedez }
1081aa9a4eaSClément Guedez 
1091aa9a4eaSClément Guedez 
1101aa9a4eaSClément Guedez 
111a5ce8890STakashi Iwai #define stac9460_dac_mute_info		snd_ctl_boolean_mono_info
112f6cdab5fSClement Guedez 
stac9460_dac_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)113f6cdab5fSClement Guedez static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
114f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
115f6cdab5fSClement Guedez {
116f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
1171aa9a4eaSClément Guedez 	struct wtm_spec *spec = ice->spec;
118f6cdab5fSClement Guedez 	unsigned char val;
119f6cdab5fSClement Guedez 	int idx, id;
120f6cdab5fSClement Guedez 
1211aa9a4eaSClément Guedez 	mutex_lock(&spec->mute_mutex);
1221aa9a4eaSClément Guedez 
123f6cdab5fSClement Guedez 	if (kcontrol->private_value) {
124f6cdab5fSClement Guedez 		idx = STAC946X_MASTER_VOLUME;
125f6cdab5fSClement Guedez 		id = 0;
126f6cdab5fSClement Guedez 	} else {
127f6cdab5fSClement Guedez 		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
128f6cdab5fSClement Guedez 		idx = id + STAC946X_LF_VOLUME;
129f6cdab5fSClement Guedez 	}
130f6cdab5fSClement Guedez 	if (id < 6)
131f6cdab5fSClement Guedez 		val = stac9460_get(ice, idx);
132f6cdab5fSClement Guedez 	else
133f6cdab5fSClement Guedez 		val = stac9460_2_get(ice, idx - 6);
134f6cdab5fSClement Guedez 	ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
1351aa9a4eaSClément Guedez 
1361aa9a4eaSClément Guedez 	mutex_unlock(&spec->mute_mutex);
137f6cdab5fSClement Guedez 	return 0;
138f6cdab5fSClement Guedez }
139f6cdab5fSClement Guedez 
stac9460_dac_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)140f6cdab5fSClement Guedez static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
141f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
142f6cdab5fSClement Guedez {
143f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
144f6cdab5fSClement Guedez 	unsigned char new, old;
145f6cdab5fSClement Guedez 	int id, idx;
146f6cdab5fSClement Guedez 	int change;
147f6cdab5fSClement Guedez 
148f6cdab5fSClement Guedez 	if (kcontrol->private_value) {
149f6cdab5fSClement Guedez 		idx = STAC946X_MASTER_VOLUME;
150f6cdab5fSClement Guedez 		old = stac9460_get(ice, idx);
151f6cdab5fSClement Guedez 		new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
152f6cdab5fSClement Guedez 							(old & ~0x80);
153f6cdab5fSClement Guedez 		change = (new != old);
154f6cdab5fSClement Guedez 		if (change) {
155f6cdab5fSClement Guedez 			stac9460_put(ice, idx, new);
156f6cdab5fSClement Guedez 			stac9460_2_put(ice, idx, new);
157f6cdab5fSClement Guedez 		}
158f6cdab5fSClement Guedez 	} else {
159f6cdab5fSClement Guedez 		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
160f6cdab5fSClement Guedez 		idx = id + STAC946X_LF_VOLUME;
161f6cdab5fSClement Guedez 		if (id < 6)
162f6cdab5fSClement Guedez 			old = stac9460_get(ice, idx);
163f6cdab5fSClement Guedez 		else
164f6cdab5fSClement Guedez 			old = stac9460_2_get(ice, idx - 6);
165f6cdab5fSClement Guedez 		new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
166f6cdab5fSClement Guedez 							(old & ~0x80);
167f6cdab5fSClement Guedez 		change = (new != old);
168f6cdab5fSClement Guedez 		if (change) {
169f6cdab5fSClement Guedez 			if (id < 6)
170f6cdab5fSClement Guedez 				stac9460_put(ice, idx, new);
171f6cdab5fSClement Guedez 			else
172f6cdab5fSClement Guedez 				stac9460_2_put(ice, idx - 6, new);
173f6cdab5fSClement Guedez 		}
174f6cdab5fSClement Guedez 	}
175f6cdab5fSClement Guedez 	return change;
176f6cdab5fSClement Guedez }
177f6cdab5fSClement Guedez 
178f6cdab5fSClement Guedez /*
179f6cdab5fSClement Guedez  * 	DAC volume attenuation mixer control
180f6cdab5fSClement Guedez  */
stac9460_dac_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)181f6cdab5fSClement Guedez static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,
182f6cdab5fSClement Guedez 				struct snd_ctl_elem_info *uinfo)
183f6cdab5fSClement Guedez {
184f6cdab5fSClement Guedez 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
185f6cdab5fSClement Guedez 	uinfo->count = 1;
186f6cdab5fSClement Guedez 	uinfo->value.integer.min = 0;			/* mute */
187f6cdab5fSClement Guedez 	uinfo->value.integer.max = 0x7f;		/* 0dB */
188f6cdab5fSClement Guedez 	return 0;
189f6cdab5fSClement Guedez }
190f6cdab5fSClement Guedez 
stac9460_dac_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)191f6cdab5fSClement Guedez static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,
192f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
193f6cdab5fSClement Guedez {
194f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
195f6cdab5fSClement Guedez 	int idx, id;
196f6cdab5fSClement Guedez 	unsigned char vol;
197f6cdab5fSClement Guedez 
198f6cdab5fSClement Guedez 	if (kcontrol->private_value) {
199f6cdab5fSClement Guedez 		idx = STAC946X_MASTER_VOLUME;
200f6cdab5fSClement Guedez 		id = 0;
201f6cdab5fSClement Guedez 	} else {
202f6cdab5fSClement Guedez 		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
203f6cdab5fSClement Guedez 		idx = id + STAC946X_LF_VOLUME;
204f6cdab5fSClement Guedez 	}
205f6cdab5fSClement Guedez 	if (id < 6)
206f6cdab5fSClement Guedez 		vol = stac9460_get(ice, idx) & 0x7f;
207f6cdab5fSClement Guedez 	else
208f6cdab5fSClement Guedez 		vol = stac9460_2_get(ice, idx - 6) & 0x7f;
209f6cdab5fSClement Guedez 	ucontrol->value.integer.value[0] = 0x7f - vol;
210f6cdab5fSClement Guedez 	return 0;
211f6cdab5fSClement Guedez }
212f6cdab5fSClement Guedez 
stac9460_dac_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)213f6cdab5fSClement Guedez static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
214f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
215f6cdab5fSClement Guedez {
216f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
217f6cdab5fSClement Guedez 	int idx, id;
218f6cdab5fSClement Guedez 	unsigned char tmp, ovol, nvol;
219f6cdab5fSClement Guedez 	int change;
220f6cdab5fSClement Guedez 
221f6cdab5fSClement Guedez 	if (kcontrol->private_value) {
222f6cdab5fSClement Guedez 		idx = STAC946X_MASTER_VOLUME;
2239cd17cd2STakashi Iwai 		nvol = ucontrol->value.integer.value[0] & 0x7f;
224f6cdab5fSClement Guedez 		tmp = stac9460_get(ice, idx);
225f6cdab5fSClement Guedez 		ovol = 0x7f - (tmp & 0x7f);
226f6cdab5fSClement Guedez 		change = (ovol != nvol);
227f6cdab5fSClement Guedez 		if (change) {
228f6cdab5fSClement Guedez 			stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
229f6cdab5fSClement Guedez 			stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
230f6cdab5fSClement Guedez 		}
231f6cdab5fSClement Guedez 	} else {
232f6cdab5fSClement Guedez 		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
233f6cdab5fSClement Guedez 		idx = id + STAC946X_LF_VOLUME;
2349cd17cd2STakashi Iwai 		nvol = ucontrol->value.integer.value[0] & 0x7f;
235f6cdab5fSClement Guedez 		if (id < 6)
236f6cdab5fSClement Guedez 			tmp = stac9460_get(ice, idx);
237f6cdab5fSClement Guedez 		else
238f6cdab5fSClement Guedez 			tmp = stac9460_2_get(ice, idx - 6);
239f6cdab5fSClement Guedez 		ovol = 0x7f - (tmp & 0x7f);
240f6cdab5fSClement Guedez 		change = (ovol != nvol);
241f6cdab5fSClement Guedez 		if (change) {
242f6cdab5fSClement Guedez 			if (id < 6)
243f6cdab5fSClement Guedez 				stac9460_put(ice, idx, (0x7f - nvol) |
244f6cdab5fSClement Guedez 							(tmp & 0x80));
245f6cdab5fSClement Guedez 			else
246f6cdab5fSClement Guedez 				stac9460_2_put(ice, idx-6, (0x7f - nvol) |
247f6cdab5fSClement Guedez 							(tmp & 0x80));
248f6cdab5fSClement Guedez 		}
249f6cdab5fSClement Guedez 	}
250f6cdab5fSClement Guedez 	return change;
251f6cdab5fSClement Guedez }
252f6cdab5fSClement Guedez 
253f6cdab5fSClement Guedez /*
254f6cdab5fSClement Guedez  * ADC mute control
255f6cdab5fSClement Guedez  */
256a5ce8890STakashi Iwai #define stac9460_adc_mute_info		snd_ctl_boolean_stereo_info
257f6cdab5fSClement Guedez 
stac9460_adc_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)258f6cdab5fSClement Guedez static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,
259f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
260f6cdab5fSClement Guedez {
261f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
262f6cdab5fSClement Guedez 	unsigned char val;
263f6cdab5fSClement Guedez 	int i, id;
264f6cdab5fSClement Guedez 
265f6cdab5fSClement Guedez 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
266f6cdab5fSClement Guedez 	if (id == 0) {
267f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
268f6cdab5fSClement Guedez 			val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i);
269f6cdab5fSClement Guedez 			ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
270f6cdab5fSClement Guedez 		}
271f6cdab5fSClement Guedez 	} else {
272f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
273f6cdab5fSClement Guedez 			val = stac9460_2_get(ice, STAC946X_MIC_L_VOLUME + i);
274f6cdab5fSClement Guedez 			ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
275f6cdab5fSClement Guedez 		}
276f6cdab5fSClement Guedez 	}
277f6cdab5fSClement Guedez 	return 0;
278f6cdab5fSClement Guedez }
279f6cdab5fSClement Guedez 
stac9460_adc_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)280f6cdab5fSClement Guedez static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
281f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
282f6cdab5fSClement Guedez {
283f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
284f6cdab5fSClement Guedez 	unsigned char new, old;
285f6cdab5fSClement Guedez 	int i, reg, id;
286f6cdab5fSClement Guedez 	int change;
287f6cdab5fSClement Guedez 
288f6cdab5fSClement Guedez 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
289f6cdab5fSClement Guedez 	if (id == 0) {
290f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
291f6cdab5fSClement Guedez 			reg = STAC946X_MIC_L_VOLUME + i;
292f6cdab5fSClement Guedez 			old = stac9460_get(ice, reg);
293f6cdab5fSClement Guedez 			new = (~ucontrol->value.integer.value[i]<<7&0x80) |
294f6cdab5fSClement Guedez 								(old&~0x80);
295f6cdab5fSClement Guedez 			change = (new != old);
296f6cdab5fSClement Guedez 			if (change)
297f6cdab5fSClement Guedez 				stac9460_put(ice, reg, new);
298f6cdab5fSClement Guedez 		}
299f6cdab5fSClement Guedez 	} else {
300f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
301f6cdab5fSClement Guedez 			reg = STAC946X_MIC_L_VOLUME + i;
302f6cdab5fSClement Guedez 			old = stac9460_2_get(ice, reg);
303f6cdab5fSClement Guedez 			new = (~ucontrol->value.integer.value[i]<<7&0x80) |
304f6cdab5fSClement Guedez 								(old&~0x80);
305f6cdab5fSClement Guedez 			change = (new != old);
306f6cdab5fSClement Guedez 			if (change)
307f6cdab5fSClement Guedez 				stac9460_2_put(ice, reg, new);
308f6cdab5fSClement Guedez 		}
309f6cdab5fSClement Guedez 	}
310f6cdab5fSClement Guedez 	return change;
311f6cdab5fSClement Guedez }
312f6cdab5fSClement Guedez 
313f6cdab5fSClement Guedez /*
314f6cdab5fSClement Guedez  *ADC gain mixer control
315f6cdab5fSClement Guedez  */
stac9460_adc_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)316f6cdab5fSClement Guedez static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,
317f6cdab5fSClement Guedez 				struct snd_ctl_elem_info *uinfo)
318f6cdab5fSClement Guedez {
319f6cdab5fSClement Guedez 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
320f6cdab5fSClement Guedez 	uinfo->count = 2;
321f6cdab5fSClement Guedez 	uinfo->value.integer.min = 0;		/* 0dB */
322f6cdab5fSClement Guedez 	uinfo->value.integer.max = 0x0f;	/* 22.5dB */
323f6cdab5fSClement Guedez 	return 0;
324f6cdab5fSClement Guedez }
325f6cdab5fSClement Guedez 
stac9460_adc_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)326f6cdab5fSClement Guedez static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,
327f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
328f6cdab5fSClement Guedez {
329f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
330f6cdab5fSClement Guedez 	int i, reg, id;
331f6cdab5fSClement Guedez 	unsigned char vol;
332f6cdab5fSClement Guedez 
333f6cdab5fSClement Guedez 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
334f6cdab5fSClement Guedez 	if (id == 0) {
335f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
336f6cdab5fSClement Guedez 			reg = STAC946X_MIC_L_VOLUME + i;
337f6cdab5fSClement Guedez 			vol = stac9460_get(ice, reg) & 0x0f;
338f6cdab5fSClement Guedez 			ucontrol->value.integer.value[i] = 0x0f - vol;
339f6cdab5fSClement Guedez 		}
340f6cdab5fSClement Guedez 	} else {
341f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
342f6cdab5fSClement Guedez 			reg = STAC946X_MIC_L_VOLUME + i;
343f6cdab5fSClement Guedez 			vol = stac9460_2_get(ice, reg) & 0x0f;
344f6cdab5fSClement Guedez 			ucontrol->value.integer.value[i] = 0x0f - vol;
345f6cdab5fSClement Guedez 		}
346f6cdab5fSClement Guedez 	}
347f6cdab5fSClement Guedez 	return 0;
348f6cdab5fSClement Guedez }
349f6cdab5fSClement Guedez 
stac9460_adc_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)350f6cdab5fSClement Guedez static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
351f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
352f6cdab5fSClement Guedez {
353f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
354f6cdab5fSClement Guedez 	int i, reg, id;
355f6cdab5fSClement Guedez 	unsigned char ovol, nvol;
356f6cdab5fSClement Guedez 	int change;
357f6cdab5fSClement Guedez 
358f6cdab5fSClement Guedez 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
359f6cdab5fSClement Guedez 	if (id == 0) {
360f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
361f6cdab5fSClement Guedez 			reg = STAC946X_MIC_L_VOLUME + i;
3629cd17cd2STakashi Iwai 			nvol = ucontrol->value.integer.value[i] & 0x0f;
363f6cdab5fSClement Guedez 			ovol = 0x0f - stac9460_get(ice, reg);
364f6cdab5fSClement Guedez 			change = ((ovol & 0x0f) != nvol);
365f6cdab5fSClement Guedez 			if (change)
366f6cdab5fSClement Guedez 				stac9460_put(ice, reg, (0x0f - nvol) |
367f6cdab5fSClement Guedez 							(ovol & ~0x0f));
368f6cdab5fSClement Guedez 		}
369f6cdab5fSClement Guedez 	} else {
370f6cdab5fSClement Guedez 		for (i = 0; i < 2; ++i) {
371f6cdab5fSClement Guedez 			reg = STAC946X_MIC_L_VOLUME + i;
3729cd17cd2STakashi Iwai 			nvol = ucontrol->value.integer.value[i] & 0x0f;
373f6cdab5fSClement Guedez 			ovol = 0x0f - stac9460_2_get(ice, reg);
374f6cdab5fSClement Guedez 			change = ((ovol & 0x0f) != nvol);
375f6cdab5fSClement Guedez 			if (change)
376f6cdab5fSClement Guedez 				stac9460_2_put(ice, reg, (0x0f - nvol) |
377f6cdab5fSClement Guedez 							(ovol & ~0x0f));
378f6cdab5fSClement Guedez 		}
379f6cdab5fSClement Guedez 	}
380f6cdab5fSClement Guedez 	return change;
381f6cdab5fSClement Guedez }
382f6cdab5fSClement Guedez 
383f6cdab5fSClement Guedez /*
384f6cdab5fSClement Guedez  * MIC / LINE switch fonction
385f6cdab5fSClement Guedez  */
stac9460_mic_sw_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)386ae8a9a11SClément Guedez static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
387ae8a9a11SClément Guedez 				struct snd_ctl_elem_info *uinfo)
388ae8a9a11SClément Guedez {
389ae8a9a11SClément Guedez 	static const char * const texts[2] = { "Line In", "Mic" };
390f6cdab5fSClement Guedez 
391ae8a9a11SClément Guedez 	return snd_ctl_enum_info(uinfo, 1, 2, texts);
392ae8a9a11SClément Guedez }
393ae8a9a11SClément Guedez 
394f6cdab5fSClement Guedez 
stac9460_mic_sw_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)395f6cdab5fSClement Guedez static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
396f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
397f6cdab5fSClement Guedez {
398f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
399f6cdab5fSClement Guedez 	unsigned char val;
400f6cdab5fSClement Guedez 	int id;
401f6cdab5fSClement Guedez 
402f6cdab5fSClement Guedez 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
403f6cdab5fSClement Guedez 	if (id == 0)
404f6cdab5fSClement Guedez 		val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
405f6cdab5fSClement Guedez 	else
406f6cdab5fSClement Guedez 		val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
407ae8a9a11SClément Guedez 	ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1;
408f6cdab5fSClement Guedez 	return 0;
409f6cdab5fSClement Guedez }
410f6cdab5fSClement Guedez 
stac9460_mic_sw_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)411f6cdab5fSClement Guedez static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
412f6cdab5fSClement Guedez 				struct snd_ctl_elem_value *ucontrol)
413f6cdab5fSClement Guedez {
414f6cdab5fSClement Guedez 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
415f6cdab5fSClement Guedez 	unsigned char new, old;
416f6cdab5fSClement Guedez 	int change, id;
417f6cdab5fSClement Guedez 
418f6cdab5fSClement Guedez 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
419f6cdab5fSClement Guedez 	if (id == 0)
420f6cdab5fSClement Guedez 		old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
421f6cdab5fSClement Guedez 	else
422f6cdab5fSClement Guedez 		old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
423ae8a9a11SClément Guedez 	new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80);
424f6cdab5fSClement Guedez 	change = (new != old);
425f6cdab5fSClement Guedez 	if (change) {
426f6cdab5fSClement Guedez 		if (id == 0)
427f6cdab5fSClement Guedez 			stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
428f6cdab5fSClement Guedez 		else
429f6cdab5fSClement Guedez 			stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
430f6cdab5fSClement Guedez 	}
431f6cdab5fSClement Guedez 	return change;
432f6cdab5fSClement Guedez }
433f6cdab5fSClement Guedez 
43416ddbe73SClément Guedez 
4351aa9a4eaSClément Guedez /*
4361aa9a4eaSClément Guedez  * Handler for setting correct codec rate - called when rate change is detected
4371aa9a4eaSClément Guedez  */
stac9460_set_rate_val(struct snd_ice1712 * ice,unsigned int rate)4381aa9a4eaSClément Guedez static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
4391aa9a4eaSClément Guedez {
4401aa9a4eaSClément Guedez 	unsigned char old, new;
4411aa9a4eaSClément Guedez 	unsigned short int changed;
4421aa9a4eaSClément Guedez 	struct wtm_spec *spec = ice->spec;
4431aa9a4eaSClément Guedez 
4441aa9a4eaSClément Guedez 	if (rate == 0)  /* no hint - S/PDIF input is master, simply return */
4451aa9a4eaSClément Guedez 		return;
4461aa9a4eaSClément Guedez 	else if (rate <= 48000)
4471aa9a4eaSClément Guedez 		new = 0x08;     /* 256x, base rate mode */
4481aa9a4eaSClément Guedez 	else if (rate <= 96000)
4491aa9a4eaSClément Guedez 		new = 0x11;     /* 256x, mid rate mode */
4501aa9a4eaSClément Guedez 	else
4511aa9a4eaSClément Guedez 		new = 0x12;     /* 128x, high rate mode */
4521aa9a4eaSClément Guedez 
4531aa9a4eaSClément Guedez 	old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
4541aa9a4eaSClément Guedez 	if (old == new)
4551aa9a4eaSClément Guedez 		return;
4561aa9a4eaSClément Guedez 	/* change detected, setting master clock, muting first */
4571aa9a4eaSClément Guedez 	/* due to possible conflicts with mute controls - mutexing */
4581aa9a4eaSClément Guedez 	mutex_lock(&spec->mute_mutex);
4591aa9a4eaSClément Guedez 	/* we have to remember current mute status for each DAC */
4601aa9a4eaSClément Guedez 	changed = 0xFFFF;
4611aa9a4eaSClément Guedez 	stac9460_dac_mute_all(ice, 0, &changed);
4621aa9a4eaSClément Guedez 	/*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
4631aa9a4eaSClément Guedez 	stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
4641aa9a4eaSClément Guedez 	stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new);
4651aa9a4eaSClément Guedez 	udelay(10);
4661aa9a4eaSClément Guedez 	/* unmuting - only originally unmuted dacs -
4671aa9a4eaSClément Guedez 	* i.e. those changed when muting */
4681aa9a4eaSClément Guedez 	stac9460_dac_mute_all(ice, 1, &changed);
4691aa9a4eaSClément Guedez 	mutex_unlock(&spec->mute_mutex);
4701aa9a4eaSClément Guedez }
4711aa9a4eaSClément Guedez 
4721aa9a4eaSClément Guedez 
47316ddbe73SClément Guedez /*Limits value in dB for fader*/
47416ddbe73SClément Guedez static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
47516ddbe73SClément Guedez static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
47616ddbe73SClément Guedez 
477f6cdab5fSClement Guedez /*
478f6cdab5fSClement Guedez  * Control tabs
479f6cdab5fSClement Guedez  */
480b4e5e707STakashi Iwai static const struct snd_kcontrol_new stac9640_controls[] = {
481f6cdab5fSClement Guedez 	{
482f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
48316ddbe73SClément Guedez 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
48416ddbe73SClément Guedez 			    SNDRV_CTL_ELEM_ACCESS_TLV_READ),
485f6cdab5fSClement Guedez 		.name = "Master Playback Switch",
486f6cdab5fSClement Guedez 		.info = stac9460_dac_mute_info,
487f6cdab5fSClement Guedez 		.get = stac9460_dac_mute_get,
488f6cdab5fSClement Guedez 		.put = stac9460_dac_mute_put,
48916ddbe73SClément Guedez 		.private_value = 1,
49016ddbe73SClément Guedez 		.tlv = { .p = db_scale_dac }
491f6cdab5fSClement Guedez 	},
492f6cdab5fSClement Guedez 	{
493f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
494f6cdab5fSClement Guedez 		.name = "Master Playback Volume",
495f6cdab5fSClement Guedez 		.info = stac9460_dac_vol_info,
496f6cdab5fSClement Guedez 		.get = stac9460_dac_vol_get,
497f6cdab5fSClement Guedez 		.put = stac9460_dac_vol_put,
498f6cdab5fSClement Guedez 		.private_value = 1,
499f6cdab5fSClement Guedez 	},
500f6cdab5fSClement Guedez 	{
501f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
502ae8a9a11SClément Guedez 		.name = "MIC/Line Input Enum",
503f6cdab5fSClement Guedez 		.count = 2,
504f6cdab5fSClement Guedez 		.info = stac9460_mic_sw_info,
505f6cdab5fSClement Guedez 		.get = stac9460_mic_sw_get,
506f6cdab5fSClement Guedez 		.put = stac9460_mic_sw_put,
507f6cdab5fSClement Guedez 
508f6cdab5fSClement Guedez 	},
509f6cdab5fSClement Guedez 	{
510f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
511f6cdab5fSClement Guedez 		.name = "DAC Switch",
512f6cdab5fSClement Guedez 		.count = 8,
513f6cdab5fSClement Guedez 		.info = stac9460_dac_mute_info,
514f6cdab5fSClement Guedez 		.get = stac9460_dac_mute_get,
515f6cdab5fSClement Guedez 		.put = stac9460_dac_mute_put,
516f6cdab5fSClement Guedez 	},
517f6cdab5fSClement Guedez 	{
518f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
51916ddbe73SClément Guedez 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
52016ddbe73SClément Guedez 			    SNDRV_CTL_ELEM_ACCESS_TLV_READ),
52116ddbe73SClément Guedez 
522f6cdab5fSClement Guedez 		.name = "DAC Volume",
523f6cdab5fSClement Guedez 		.count = 8,
524f6cdab5fSClement Guedez 		.info = stac9460_dac_vol_info,
525f6cdab5fSClement Guedez 		.get = stac9460_dac_vol_get,
526f6cdab5fSClement Guedez 		.put = stac9460_dac_vol_put,
52716ddbe73SClément Guedez 		.tlv = { .p = db_scale_dac }
528f6cdab5fSClement Guedez 	},
529f6cdab5fSClement Guedez 	{
530f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
531f6cdab5fSClement Guedez 		.name = "ADC Switch",
532f6cdab5fSClement Guedez 		.count = 2,
533f6cdab5fSClement Guedez 		.info = stac9460_adc_mute_info,
534f6cdab5fSClement Guedez 		.get = stac9460_adc_mute_get,
535f6cdab5fSClement Guedez 		.put = stac9460_adc_mute_put,
536f6cdab5fSClement Guedez 	},
537f6cdab5fSClement Guedez 	{
538f6cdab5fSClement Guedez 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
53916ddbe73SClément Guedez 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
54016ddbe73SClément Guedez 			    SNDRV_CTL_ELEM_ACCESS_TLV_READ),
54116ddbe73SClément Guedez 
542f6cdab5fSClement Guedez 		.name = "ADC Volume",
543f6cdab5fSClement Guedez 		.count = 2,
544f6cdab5fSClement Guedez 		.info = stac9460_adc_vol_info,
545f6cdab5fSClement Guedez 		.get = stac9460_adc_vol_get,
546f6cdab5fSClement Guedez 		.put = stac9460_adc_vol_put,
54716ddbe73SClément Guedez 		.tlv = { .p = db_scale_adc }
548f6cdab5fSClement Guedez 	}
549f6cdab5fSClement Guedez };
550f6cdab5fSClement Guedez 
551f6cdab5fSClement Guedez 
552f6cdab5fSClement Guedez 
553f6cdab5fSClement Guedez /*INIT*/
wtm_add_controls(struct snd_ice1712 * ice)554e23e7a14SBill Pemberton static int wtm_add_controls(struct snd_ice1712 *ice)
555f6cdab5fSClement Guedez {
556f6cdab5fSClement Guedez 	unsigned int i;
557f6cdab5fSClement Guedez 	int err;
558f6cdab5fSClement Guedez 
559f6cdab5fSClement Guedez 	for (i = 0; i < ARRAY_SIZE(stac9640_controls); i++) {
560f6cdab5fSClement Guedez 		err = snd_ctl_add(ice->card,
561f6cdab5fSClement Guedez 				snd_ctl_new1(&stac9640_controls[i], ice));
562f6cdab5fSClement Guedez 		if (err < 0)
563f6cdab5fSClement Guedez 			return err;
564f6cdab5fSClement Guedez 	}
565f6cdab5fSClement Guedez 	return 0;
566f6cdab5fSClement Guedez }
567f6cdab5fSClement Guedez 
wtm_init(struct snd_ice1712 * ice)568e23e7a14SBill Pemberton static int wtm_init(struct snd_ice1712 *ice)
569f6cdab5fSClement Guedez {
570*f16a4e96STakashi Iwai 	static const unsigned short stac_inits_wtm[] = {
571f6cdab5fSClement Guedez 		STAC946X_RESET, 0,
5721aa9a4eaSClément Guedez 		STAC946X_MASTER_CLOCKING, 0x11,
573f6cdab5fSClement Guedez 		(unsigned short)-1
574f6cdab5fSClement Guedez 	};
575*f16a4e96STakashi Iwai 	const unsigned short *p;
5761aa9a4eaSClément Guedez 	struct wtm_spec *spec;
577f6cdab5fSClement Guedez 
578f6cdab5fSClement Guedez 	/*WTM 192M*/
579f6cdab5fSClement Guedez 	ice->num_total_dacs = 8;
580f6cdab5fSClement Guedez 	ice->num_total_adcs = 4;
581f6cdab5fSClement Guedez 	ice->force_rdma1 = 1;
582f6cdab5fSClement Guedez 
5831aa9a4eaSClément Guedez 	/*init mutex for dac mute conflict*/
5841aa9a4eaSClément Guedez 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
5851aa9a4eaSClément Guedez 	if (!spec)
5861aa9a4eaSClément Guedez 		return -ENOMEM;
5871aa9a4eaSClément Guedez 	ice->spec = spec;
5881aa9a4eaSClément Guedez 	mutex_init(&spec->mute_mutex);
5891aa9a4eaSClément Guedez 
5901aa9a4eaSClément Guedez 
591f6cdab5fSClement Guedez 	/*initialize codec*/
592b56df151SClément Guedez 	p = stac_inits_wtm;
593f6cdab5fSClement Guedez 	for (; *p != (unsigned short)-1; p += 2) {
594f6cdab5fSClement Guedez 		stac9460_put(ice, p[0], p[1]);
595f6cdab5fSClement Guedez 		stac9460_2_put(ice, p[0], p[1]);
596f6cdab5fSClement Guedez 	}
5971aa9a4eaSClément Guedez 	ice->gpio.set_pro_rate = stac9460_set_rate_val;
598f6cdab5fSClement Guedez 	return 0;
599f6cdab5fSClement Guedez }
600f6cdab5fSClement Guedez 
601f6cdab5fSClement Guedez 
602*f16a4e96STakashi Iwai static const unsigned char wtm_eeprom[] = {
603f8a8b3a8SClément Guedez 	[ICE_EEP2_SYSCONF]      = 0x67, /*SYSCONF: clock 192KHz, mpu401,
604f8a8b3a8SClément Guedez 							4ADC, 8DAC */
6057127744aSClément Guedez 	[ICE_EEP2_ACLINK]       = 0x80, /* ACLINK : I2S */
6067127744aSClément Guedez 	[ICE_EEP2_I2S]          = 0xf8, /* I2S: vol; 96k, 24bit, 192k */
6077127744aSClément Guedez 	[ICE_EEP2_SPDIF]        = 0xc1, /*SPDIF: out-en, spidf ext out*/
6087127744aSClément Guedez 	[ICE_EEP2_GPIO_DIR]     = 0x9f,
6097127744aSClément Guedez 	[ICE_EEP2_GPIO_DIR1]    = 0xff,
6107127744aSClément Guedez 	[ICE_EEP2_GPIO_DIR2]    = 0x7f,
6117127744aSClément Guedez 	[ICE_EEP2_GPIO_MASK]    = 0x9f,
6127127744aSClément Guedez 	[ICE_EEP2_GPIO_MASK1]   = 0xff,
6137127744aSClément Guedez 	[ICE_EEP2_GPIO_MASK2]   = 0x7f,
6147127744aSClément Guedez 	[ICE_EEP2_GPIO_STATE]   = 0x16,
6157127744aSClément Guedez 	[ICE_EEP2_GPIO_STATE1]  = 0x80,
6167127744aSClément Guedez 	[ICE_EEP2_GPIO_STATE2]  = 0x00,
617f6cdab5fSClement Guedez };
618f6cdab5fSClement Guedez 
619f6cdab5fSClement Guedez 
620f6cdab5fSClement Guedez /*entry point*/
621e23e7a14SBill Pemberton struct snd_ice1712_card_info snd_vt1724_wtm_cards[] = {
622f6cdab5fSClement Guedez 	{
623f6cdab5fSClement Guedez 		.subvendor = VT1724_SUBDEVICE_WTM,
624f6cdab5fSClement Guedez 		.name = "ESI Waveterminal 192M",
625f6cdab5fSClement Guedez 		.model = "WT192M",
626f6cdab5fSClement Guedez 		.chip_init = wtm_init,
627f6cdab5fSClement Guedez 		.build_controls = wtm_add_controls,
628f6cdab5fSClement Guedez 		.eeprom_size = sizeof(wtm_eeprom),
629f6cdab5fSClement Guedez 		.eeprom_data = wtm_eeprom,
630f6cdab5fSClement Guedez 	},
631f6cdab5fSClement Guedez 	{} /*terminator*/
632f6cdab5fSClement Guedez };
633