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