diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-05-06 11:33:19 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-05-06 11:33:19 -0400 |
commit | 72cbfd45fac627de4bd38c203baaac40cd26477c (patch) | |
tree | 830a5958d85a399c62580ab6941e304a769706d6 /sound/pci | |
parent | 2bf864ac963c5126a9c7c3dce0958390fb612270 (diff) |
ALSA: ice1724 - Add ESI Maya44 support
Added the support for ESI Maya44 board to ice1724 driver.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/ice1712/Makefile | 2 | ||||
-rw-r--r-- | sound/pci/ice1712/ice1724.c | 3 | ||||
-rw-r--r-- | sound/pci/ice1712/maya44.c | 779 | ||||
-rw-r--r-- | sound/pci/ice1712/maya44.h | 10 |
4 files changed, 793 insertions, 1 deletions
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index f99fe089495d..536eae2ccf94 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile | |||
@@ -5,7 +5,7 @@ | |||
5 | 5 | ||
6 | snd-ice17xx-ak4xxx-objs := ak4xxx.o | 6 | snd-ice17xx-ak4xxx-objs := ak4xxx.o |
7 | snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o | 7 | snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o |
8 | snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o | 8 | snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o |
9 | 9 | ||
10 | # Toplevel Module Dependency | 10 | # Toplevel Module Dependency |
11 | obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o | 11 | obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o |
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 5a735eed317d..36ade77cf371 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include "prodigy192.h" | 49 | #include "prodigy192.h" |
50 | #include "prodigy_hifi.h" | 50 | #include "prodigy_hifi.h" |
51 | #include "juli.h" | 51 | #include "juli.h" |
52 | #include "maya44.h" | ||
52 | #include "phase.h" | 53 | #include "phase.h" |
53 | #include "wtm.h" | 54 | #include "wtm.h" |
54 | #include "se.h" | 55 | #include "se.h" |
@@ -65,6 +66,7 @@ MODULE_SUPPORTED_DEVICE("{" | |||
65 | PRODIGY192_DEVICE_DESC | 66 | PRODIGY192_DEVICE_DESC |
66 | PRODIGY_HIFI_DEVICE_DESC | 67 | PRODIGY_HIFI_DEVICE_DESC |
67 | JULI_DEVICE_DESC | 68 | JULI_DEVICE_DESC |
69 | MAYA44_DEVICE_DESC | ||
68 | PHASE_DEVICE_DESC | 70 | PHASE_DEVICE_DESC |
69 | WTM_DEVICE_DESC | 71 | WTM_DEVICE_DESC |
70 | SE_DEVICE_DESC | 72 | SE_DEVICE_DESC |
@@ -2125,6 +2127,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { | |||
2125 | snd_vt1724_prodigy_hifi_cards, | 2127 | snd_vt1724_prodigy_hifi_cards, |
2126 | snd_vt1724_prodigy192_cards, | 2128 | snd_vt1724_prodigy192_cards, |
2127 | snd_vt1724_juli_cards, | 2129 | snd_vt1724_juli_cards, |
2130 | snd_vt1724_maya44_cards, | ||
2128 | snd_vt1724_phase_cards, | 2131 | snd_vt1724_phase_cards, |
2129 | snd_vt1724_wtm_cards, | 2132 | snd_vt1724_wtm_cards, |
2130 | snd_vt1724_se_cards, | 2133 | snd_vt1724_se_cards, |
diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c new file mode 100644 index 000000000000..3e1c20ae2f1c --- /dev/null +++ b/sound/pci/ice1712/maya44.c | |||
@@ -0,0 +1,779 @@ | |||
1 | /* | ||
2 | * ALSA driver for ICEnsemble VT1724 (Envy24HT) | ||
3 | * | ||
4 | * Lowlevel functions for ESI Maya44 cards | ||
5 | * | ||
6 | * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de> | ||
7 | * Based on the patches by Rainer Zimmermann <mail@lightshed.de> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/control.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/tlv.h> | ||
32 | |||
33 | #include "ice1712.h" | ||
34 | #include "envy24ht.h" | ||
35 | #include "maya44.h" | ||
36 | |||
37 | /* WM8776 register indexes */ | ||
38 | #define WM8776_REG_HEADPHONE_L 0x00 | ||
39 | #define WM8776_REG_HEADPHONE_R 0x01 | ||
40 | #define WM8776_REG_HEADPHONE_MASTER 0x02 | ||
41 | #define WM8776_REG_DAC_ATTEN_L 0x03 | ||
42 | #define WM8776_REG_DAC_ATTEN_R 0x04 | ||
43 | #define WM8776_REG_DAC_ATTEN_MASTER 0x05 | ||
44 | #define WM8776_REG_DAC_PHASE 0x06 | ||
45 | #define WM8776_REG_DAC_CONTROL 0x07 | ||
46 | #define WM8776_REG_DAC_MUTE 0x08 | ||
47 | #define WM8776_REG_DAC_DEEMPH 0x09 | ||
48 | #define WM8776_REG_DAC_IF_CONTROL 0x0a | ||
49 | #define WM8776_REG_ADC_IF_CONTROL 0x0b | ||
50 | #define WM8776_REG_MASTER_MODE_CONTROL 0x0c | ||
51 | #define WM8776_REG_POWERDOWN 0x0d | ||
52 | #define WM8776_REG_ADC_ATTEN_L 0x0e | ||
53 | #define WM8776_REG_ADC_ATTEN_R 0x0f | ||
54 | #define WM8776_REG_ADC_ALC1 0x10 | ||
55 | #define WM8776_REG_ADC_ALC2 0x11 | ||
56 | #define WM8776_REG_ADC_ALC3 0x12 | ||
57 | #define WM8776_REG_ADC_NOISE_GATE 0x13 | ||
58 | #define WM8776_REG_ADC_LIMITER 0x14 | ||
59 | #define WM8776_REG_ADC_MUX 0x15 | ||
60 | #define WM8776_REG_OUTPUT_MUX 0x16 | ||
61 | #define WM8776_REG_RESET 0x17 | ||
62 | |||
63 | #define WM8776_NUM_REGS 0x18 | ||
64 | |||
65 | /* clock ratio identifiers for snd_wm8776_set_rate() */ | ||
66 | #define WM8776_CLOCK_RATIO_128FS 0 | ||
67 | #define WM8776_CLOCK_RATIO_192FS 1 | ||
68 | #define WM8776_CLOCK_RATIO_256FS 2 | ||
69 | #define WM8776_CLOCK_RATIO_384FS 3 | ||
70 | #define WM8776_CLOCK_RATIO_512FS 4 | ||
71 | #define WM8776_CLOCK_RATIO_768FS 5 | ||
72 | |||
73 | enum { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS }; | ||
74 | enum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES }; | ||
75 | |||
76 | struct snd_wm8776 { | ||
77 | unsigned char addr; | ||
78 | unsigned short regs[WM8776_NUM_REGS]; | ||
79 | unsigned char volumes[WM_NUM_VOLS][2]; | ||
80 | unsigned int switch_bits; | ||
81 | }; | ||
82 | |||
83 | struct snd_maya44 { | ||
84 | struct snd_ice1712 *ice; | ||
85 | struct snd_wm8776 wm[2]; | ||
86 | struct mutex mutex; | ||
87 | }; | ||
88 | |||
89 | |||
90 | /* write the given register and save the data to the cache */ | ||
91 | static void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm, | ||
92 | unsigned char reg, unsigned short val) | ||
93 | { | ||
94 | /* | ||
95 | * WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB | ||
96 | * of the address field | ||
97 | */ | ||
98 | snd_vt1724_write_i2c(ice, wm->addr, | ||
99 | (reg << 1) | ((val >> 8) & 1), | ||
100 | val & 0xff); | ||
101 | wm->regs[reg] = val; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * update the given register with and/or mask and save the data to the cache | ||
106 | */ | ||
107 | static int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm, | ||
108 | unsigned char reg, | ||
109 | unsigned short mask, unsigned short val) | ||
110 | { | ||
111 | val |= wm->regs[reg] & ~mask; | ||
112 | if (val != wm->regs[reg]) { | ||
113 | wm8776_write(ice, wm, reg, val); | ||
114 | return 1; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | |||
120 | /* | ||
121 | * WM8776 volume controls | ||
122 | */ | ||
123 | |||
124 | struct maya_vol_info { | ||
125 | unsigned int maxval; /* volume range: 0..maxval */ | ||
126 | unsigned char regs[2]; /* left and right registers */ | ||
127 | unsigned short mask; /* value mask */ | ||
128 | unsigned short offset; /* zero-value offset */ | ||
129 | unsigned short mute; /* mute bit */ | ||
130 | unsigned short update; /* update bits */ | ||
131 | unsigned char mux_bits[2]; /* extra bits for ADC mute */ | ||
132 | }; | ||
133 | |||
134 | static struct maya_vol_info vol_info[WM_NUM_VOLS] = { | ||
135 | [WM_VOL_HP] = { | ||
136 | .maxval = 80, | ||
137 | .regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R }, | ||
138 | .mask = 0x7f, | ||
139 | .offset = 0x30, | ||
140 | .mute = 0x00, | ||
141 | .update = 0x180, /* update and zero-cross enable */ | ||
142 | }, | ||
143 | [WM_VOL_DAC] = { | ||
144 | .maxval = 255, | ||
145 | .regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R }, | ||
146 | .mask = 0xff, | ||
147 | .offset = 0x01, | ||
148 | .mute = 0x00, | ||
149 | .update = 0x100, /* zero-cross enable */ | ||
150 | }, | ||
151 | [WM_VOL_ADC] = { | ||
152 | .maxval = 91, | ||
153 | .regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R }, | ||
154 | .mask = 0xff, | ||
155 | .offset = 0xa5, | ||
156 | .mute = 0xa5, | ||
157 | .update = 0x100, /* update */ | ||
158 | .mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */ | ||
159 | }, | ||
160 | }; | ||
161 | |||
162 | /* | ||
163 | * dB tables | ||
164 | */ | ||
165 | /* headphone output: mute, -73..+6db (1db step) */ | ||
166 | static const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1); | ||
167 | /* DAC output: mute, -127..0db (0.5db step) */ | ||
168 | static const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1); | ||
169 | /* ADC gain: mute, -21..+24db (0.5db step) */ | ||
170 | static const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1); | ||
171 | |||
172 | static int maya_vol_info(struct snd_kcontrol *kcontrol, | ||
173 | struct snd_ctl_elem_info *uinfo) | ||
174 | { | ||
175 | unsigned int idx = kcontrol->private_value; | ||
176 | struct maya_vol_info *vol = &vol_info[idx]; | ||
177 | |||
178 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
179 | uinfo->count = 2; | ||
180 | uinfo->value.integer.min = 0; | ||
181 | uinfo->value.integer.max = vol->maxval; | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int maya_vol_get(struct snd_kcontrol *kcontrol, | ||
186 | struct snd_ctl_elem_value *ucontrol) | ||
187 | { | ||
188 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
189 | struct snd_wm8776 *wm = | ||
190 | &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; | ||
191 | unsigned int idx = kcontrol->private_value; | ||
192 | |||
193 | mutex_lock(&chip->mutex); | ||
194 | ucontrol->value.integer.value[0] = wm->volumes[idx][0]; | ||
195 | ucontrol->value.integer.value[1] = wm->volumes[idx][1]; | ||
196 | mutex_unlock(&chip->mutex); | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int maya_vol_put(struct snd_kcontrol *kcontrol, | ||
201 | struct snd_ctl_elem_value *ucontrol) | ||
202 | { | ||
203 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
204 | struct snd_wm8776 *wm = | ||
205 | &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; | ||
206 | unsigned int idx = kcontrol->private_value; | ||
207 | struct maya_vol_info *vol = &vol_info[idx]; | ||
208 | unsigned int val, data; | ||
209 | int ch, changed = 0; | ||
210 | |||
211 | mutex_lock(&chip->mutex); | ||
212 | for (ch = 0; ch < 2; ch++) { | ||
213 | val = ucontrol->value.integer.value[ch]; | ||
214 | if (val > vol->maxval) | ||
215 | val = vol->maxval; | ||
216 | if (val == wm->volumes[idx][ch]) | ||
217 | continue; | ||
218 | if (!val) | ||
219 | data = vol->mute; | ||
220 | else | ||
221 | data = (val - 1) + vol->offset; | ||
222 | data |= vol->update; | ||
223 | changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch], | ||
224 | vol->mask | vol->update, data); | ||
225 | if (vol->mux_bits[ch]) | ||
226 | wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX, | ||
227 | vol->mux_bits[ch], | ||
228 | val ? 0 : vol->mux_bits[ch]); | ||
229 | wm->volumes[idx][ch] = val; | ||
230 | } | ||
231 | mutex_unlock(&chip->mutex); | ||
232 | return changed; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * WM8776 switch controls | ||
237 | */ | ||
238 | |||
239 | #define COMPOSE_SW_VAL(idx, reg, mask) ((idx) | ((reg) << 8) | ((mask) << 16)) | ||
240 | #define GET_SW_VAL_IDX(val) ((val) & 0xff) | ||
241 | #define GET_SW_VAL_REG(val) (((val) >> 8) & 0xff) | ||
242 | #define GET_SW_VAL_MASK(val) (((val) >> 16) & 0xff) | ||
243 | |||
244 | #define maya_sw_info snd_ctl_boolean_mono_info | ||
245 | |||
246 | static int maya_sw_get(struct snd_kcontrol *kcontrol, | ||
247 | struct snd_ctl_elem_value *ucontrol) | ||
248 | { | ||
249 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
250 | struct snd_wm8776 *wm = | ||
251 | &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; | ||
252 | unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value); | ||
253 | |||
254 | ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1; | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int maya_sw_put(struct snd_kcontrol *kcontrol, | ||
259 | struct snd_ctl_elem_value *ucontrol) | ||
260 | { | ||
261 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
262 | struct snd_wm8776 *wm = | ||
263 | &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; | ||
264 | unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value); | ||
265 | unsigned int mask, val; | ||
266 | int changed; | ||
267 | |||
268 | mutex_lock(&chip->mutex); | ||
269 | mask = 1 << idx; | ||
270 | wm->switch_bits &= ~mask; | ||
271 | val = ucontrol->value.integer.value[0]; | ||
272 | if (val) | ||
273 | wm->switch_bits |= mask; | ||
274 | mask = GET_SW_VAL_MASK(kcontrol->private_value); | ||
275 | changed = wm8776_write_bits(chip->ice, wm, | ||
276 | GET_SW_VAL_REG(kcontrol->private_value), | ||
277 | mask, val ? mask : 0); | ||
278 | mutex_unlock(&chip->mutex); | ||
279 | return changed; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * GPIO pins (known ones for maya44) | ||
284 | */ | ||
285 | #define GPIO_PHANTOM_OFF 2 | ||
286 | #define GPIO_MIC_RELAY 4 | ||
287 | #define GPIO_SPDIF_IN_INV 5 | ||
288 | #define GPIO_MUST_BE_0 7 | ||
289 | |||
290 | /* | ||
291 | * GPIO switch controls | ||
292 | */ | ||
293 | |||
294 | #define COMPOSE_GPIO_VAL(shift, inv) ((shift) | ((inv) << 8)) | ||
295 | #define GET_GPIO_VAL_SHIFT(val) ((val) & 0xff) | ||
296 | #define GET_GPIO_VAL_INV(val) (((val) >> 8) & 1) | ||
297 | |||
298 | static int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask, | ||
299 | unsigned int bits) | ||
300 | { | ||
301 | unsigned int data; | ||
302 | data = snd_ice1712_gpio_read(ice); | ||
303 | if ((data & mask) == bits) | ||
304 | return 0; | ||
305 | snd_ice1712_gpio_write(ice, (data & ~mask) | bits); | ||
306 | return 1; | ||
307 | } | ||
308 | |||
309 | #define maya_gpio_sw_info snd_ctl_boolean_mono_info | ||
310 | |||
311 | static int maya_gpio_sw_get(struct snd_kcontrol *kcontrol, | ||
312 | struct snd_ctl_elem_value *ucontrol) | ||
313 | { | ||
314 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
315 | unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value); | ||
316 | unsigned int val; | ||
317 | |||
318 | val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1; | ||
319 | if (GET_GPIO_VAL_INV(kcontrol->private_value)) | ||
320 | val = !val; | ||
321 | ucontrol->value.integer.value[0] = val; | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol, | ||
326 | struct snd_ctl_elem_value *ucontrol) | ||
327 | { | ||
328 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
329 | unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value); | ||
330 | unsigned int val, mask; | ||
331 | int changed; | ||
332 | |||
333 | mutex_lock(&chip->mutex); | ||
334 | mask = 1 << shift; | ||
335 | val = ucontrol->value.integer.value[0]; | ||
336 | if (GET_GPIO_VAL_INV(kcontrol->private_value)) | ||
337 | val = !val; | ||
338 | val = val ? mask : 0; | ||
339 | changed = maya_set_gpio_bits(chip->ice, mask, val); | ||
340 | mutex_unlock(&chip->mutex); | ||
341 | return changed; | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * capture source selection | ||
346 | */ | ||
347 | |||
348 | /* known working input slots (0-4) */ | ||
349 | #define MAYA_LINE_IN 1 /* in-2 */ | ||
350 | #define MAYA_MIC_IN 4 /* in-5 */ | ||
351 | |||
352 | static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line) | ||
353 | { | ||
354 | wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX, | ||
355 | 0x1f, 1 << line); | ||
356 | } | ||
357 | |||
358 | static int maya_rec_src_info(struct snd_kcontrol *kcontrol, | ||
359 | struct snd_ctl_elem_info *uinfo) | ||
360 | { | ||
361 | static char *texts[] = { "Line", "Mic" }; | ||
362 | |||
363 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
364 | uinfo->count = 1; | ||
365 | uinfo->value.enumerated.items = ARRAY_SIZE(texts); | ||
366 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | ||
367 | uinfo->value.enumerated.item = | ||
368 | uinfo->value.enumerated.items - 1; | ||
369 | strcpy(uinfo->value.enumerated.name, | ||
370 | texts[uinfo->value.enumerated.item]); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static int maya_rec_src_get(struct snd_kcontrol *kcontrol, | ||
375 | struct snd_ctl_elem_value *ucontrol) | ||
376 | { | ||
377 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
378 | int sel; | ||
379 | |||
380 | if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY)) | ||
381 | sel = 1; | ||
382 | else | ||
383 | sel = 0; | ||
384 | ucontrol->value.enumerated.item[0] = sel; | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int maya_rec_src_put(struct snd_kcontrol *kcontrol, | ||
389 | struct snd_ctl_elem_value *ucontrol) | ||
390 | { | ||
391 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
392 | int sel = ucontrol->value.enumerated.item[0]; | ||
393 | int changed; | ||
394 | |||
395 | mutex_lock(&chip->mutex); | ||
396 | changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY, | ||
397 | sel ? GPIO_MIC_RELAY : 0); | ||
398 | wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN); | ||
399 | mutex_unlock(&chip->mutex); | ||
400 | return changed; | ||
401 | } | ||
402 | |||
403 | /* | ||
404 | * Maya44 routing switch settings have different meanings than the standard | ||
405 | * ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c). | ||
406 | */ | ||
407 | static int maya_pb_route_info(struct snd_kcontrol *kcontrol, | ||
408 | struct snd_ctl_elem_info *uinfo) | ||
409 | { | ||
410 | static char *texts[] = { | ||
411 | "PCM Out", /* 0 */ | ||
412 | "Input 1", "Input 2", "Input 3", "Input 4" | ||
413 | }; | ||
414 | |||
415 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
416 | uinfo->count = 1; | ||
417 | uinfo->value.enumerated.items = ARRAY_SIZE(texts); | ||
418 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | ||
419 | uinfo->value.enumerated.item = | ||
420 | uinfo->value.enumerated.items - 1; | ||
421 | strcpy(uinfo->value.enumerated.name, | ||
422 | texts[uinfo->value.enumerated.item]); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int maya_pb_route_shift(int idx) | ||
427 | { | ||
428 | static const unsigned char shift[10] = | ||
429 | { 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 }; | ||
430 | return shift[idx % 10]; | ||
431 | } | ||
432 | |||
433 | static int maya_pb_route_get(struct snd_kcontrol *kcontrol, | ||
434 | struct snd_ctl_elem_value *ucontrol) | ||
435 | { | ||
436 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
437 | int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); | ||
438 | ucontrol->value.enumerated.item[0] = | ||
439 | snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx)); | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static int maya_pb_route_put(struct snd_kcontrol *kcontrol, | ||
444 | struct snd_ctl_elem_value *ucontrol) | ||
445 | { | ||
446 | struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); | ||
447 | int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); | ||
448 | return snd_ice1724_put_route_val(chip->ice, | ||
449 | ucontrol->value.enumerated.item[0], | ||
450 | maya_pb_route_shift(idx)); | ||
451 | } | ||
452 | |||
453 | |||
454 | /* | ||
455 | * controls to be added | ||
456 | */ | ||
457 | |||
458 | static struct snd_kcontrol_new maya_controls[] __devinitdata = { | ||
459 | { | ||
460 | .name = "Crossmix Playback Volume", | ||
461 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
462 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
463 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
464 | .info = maya_vol_info, | ||
465 | .get = maya_vol_get, | ||
466 | .put = maya_vol_put, | ||
467 | .tlv = { .p = db_scale_hp }, | ||
468 | .private_value = WM_VOL_HP, | ||
469 | .count = 2, | ||
470 | }, | ||
471 | { | ||
472 | .name = "PCM Playback Volume", | ||
473 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
474 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
475 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
476 | .info = maya_vol_info, | ||
477 | .get = maya_vol_get, | ||
478 | .put = maya_vol_put, | ||
479 | .tlv = { .p = db_scale_dac }, | ||
480 | .private_value = WM_VOL_DAC, | ||
481 | .count = 2, | ||
482 | }, | ||
483 | { | ||
484 | .name = "Line Capture Volume", | ||
485 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
486 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
487 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
488 | .info = maya_vol_info, | ||
489 | .get = maya_vol_get, | ||
490 | .put = maya_vol_put, | ||
491 | .tlv = { .p = db_scale_adc }, | ||
492 | .private_value = WM_VOL_ADC, | ||
493 | .count = 2, | ||
494 | }, | ||
495 | { | ||
496 | .name = "PCM Playback Switch", | ||
497 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
498 | .info = maya_sw_info, | ||
499 | .get = maya_sw_get, | ||
500 | .put = maya_sw_put, | ||
501 | .private_value = COMPOSE_SW_VAL(WM_SW_DAC, | ||
502 | WM8776_REG_OUTPUT_MUX, 0x01), | ||
503 | .count = 2, | ||
504 | }, | ||
505 | { | ||
506 | .name = "Bypass Playback Switch", | ||
507 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
508 | .info = maya_sw_info, | ||
509 | .get = maya_sw_get, | ||
510 | .put = maya_sw_put, | ||
511 | .private_value = COMPOSE_SW_VAL(WM_SW_BYPASS, | ||
512 | WM8776_REG_OUTPUT_MUX, 0x04), | ||
513 | .count = 2, | ||
514 | }, | ||
515 | { | ||
516 | .name = "Capture Source", | ||
517 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
518 | .info = maya_rec_src_info, | ||
519 | .get = maya_rec_src_get, | ||
520 | .put = maya_rec_src_put, | ||
521 | }, | ||
522 | { | ||
523 | .name = "Mic Phantom Power Switch", | ||
524 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
525 | .info = maya_gpio_sw_info, | ||
526 | .get = maya_gpio_sw_get, | ||
527 | .put = maya_gpio_sw_put, | ||
528 | .private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1), | ||
529 | }, | ||
530 | { | ||
531 | .name = "SPDIF Capture Switch", | ||
532 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
533 | .info = maya_gpio_sw_info, | ||
534 | .get = maya_gpio_sw_get, | ||
535 | .put = maya_gpio_sw_put, | ||
536 | .private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1), | ||
537 | }, | ||
538 | { | ||
539 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
540 | .name = "H/W Playback Route", | ||
541 | .info = maya_pb_route_info, | ||
542 | .get = maya_pb_route_get, | ||
543 | .put = maya_pb_route_put, | ||
544 | .count = 4, /* FIXME: do controls 5-9 have any meaning? */ | ||
545 | }, | ||
546 | }; | ||
547 | |||
548 | static int __devinit maya44_add_controls(struct snd_ice1712 *ice) | ||
549 | { | ||
550 | int err, i; | ||
551 | |||
552 | for (i = 0; i < ARRAY_SIZE(maya_controls); i++) { | ||
553 | err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i], | ||
554 | ice->spec)); | ||
555 | if (err < 0) | ||
556 | return err; | ||
557 | } | ||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | |||
562 | /* | ||
563 | * initialize a wm8776 chip | ||
564 | */ | ||
565 | static void __devinit wm8776_init(struct snd_ice1712 *ice, | ||
566 | struct snd_wm8776 *wm, unsigned int addr) | ||
567 | { | ||
568 | static const unsigned short inits_wm8776[] = { | ||
569 | 0x02, 0x100, /* R2: headphone L+R muted + update */ | ||
570 | 0x05, 0x100, /* R5: DAC output L+R muted + update */ | ||
571 | 0x06, 0x000, /* R6: DAC output phase normal */ | ||
572 | 0x07, 0x091, /* R7: DAC enable zero cross detection, | ||
573 | normal output */ | ||
574 | 0x08, 0x000, /* R8: DAC soft mute off */ | ||
575 | 0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */ | ||
576 | 0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */ | ||
577 | 0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit, | ||
578 | highpass filter enabled */ | ||
579 | 0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */ | ||
580 | 0x0d, 0x000, /* R13: all power up */ | ||
581 | 0x0e, 0x100, /* R14: ADC left muted, | ||
582 | enable zero cross detection */ | ||
583 | 0x0f, 0x100, /* R15: ADC right muted, | ||
584 | enable zero cross detection */ | ||
585 | /* R16: ALC...*/ | ||
586 | 0x11, 0x000, /* R17: disable ALC */ | ||
587 | /* R18: ALC...*/ | ||
588 | /* R19: noise gate...*/ | ||
589 | 0x15, 0x000, /* R21: ADC input mux init, mute all inputs */ | ||
590 | 0x16, 0x001, /* R22: output mux, select DAC */ | ||
591 | 0xff, 0xff | ||
592 | }; | ||
593 | |||
594 | const unsigned short *ptr; | ||
595 | unsigned char reg; | ||
596 | unsigned short data; | ||
597 | |||
598 | wm->addr = addr; | ||
599 | /* enable DAC output; mute bypass, aux & all inputs */ | ||
600 | wm->switch_bits = (1 << WM_SW_DAC); | ||
601 | |||
602 | ptr = inits_wm8776; | ||
603 | while (*ptr != 0xff) { | ||
604 | reg = *ptr++; | ||
605 | data = *ptr++; | ||
606 | wm8776_write(ice, wm, reg, data); | ||
607 | } | ||
608 | } | ||
609 | |||
610 | |||
611 | /* | ||
612 | * change the rate on the WM8776 codecs. | ||
613 | * this assumes that the VT17xx's rate is changed by the calling function. | ||
614 | * NOTE: even though the WM8776's are running in slave mode and rate | ||
615 | * selection is automatic, we need to call snd_wm8776_set_rate() here | ||
616 | * to make sure some flags are set correctly. | ||
617 | */ | ||
618 | static void set_rate(struct snd_ice1712 *ice, unsigned int rate) | ||
619 | { | ||
620 | struct snd_maya44 *chip = ice->spec; | ||
621 | unsigned int ratio, adc_ratio, val; | ||
622 | int i; | ||
623 | |||
624 | switch (rate) { | ||
625 | case 192000: | ||
626 | ratio = WM8776_CLOCK_RATIO_128FS; | ||
627 | break; | ||
628 | case 176400: | ||
629 | ratio = WM8776_CLOCK_RATIO_128FS; | ||
630 | break; | ||
631 | case 96000: | ||
632 | ratio = WM8776_CLOCK_RATIO_256FS; | ||
633 | break; | ||
634 | case 88200: | ||
635 | ratio = WM8776_CLOCK_RATIO_384FS; | ||
636 | break; | ||
637 | case 48000: | ||
638 | ratio = WM8776_CLOCK_RATIO_512FS; | ||
639 | break; | ||
640 | case 44100: | ||
641 | ratio = WM8776_CLOCK_RATIO_512FS; | ||
642 | break; | ||
643 | case 32000: | ||
644 | ratio = WM8776_CLOCK_RATIO_768FS; | ||
645 | break; | ||
646 | case 0: | ||
647 | /* no hint - S/PDIF input is master, simply return */ | ||
648 | return; | ||
649 | default: | ||
650 | snd_BUG(); | ||
651 | return; | ||
652 | } | ||
653 | |||
654 | /* | ||
655 | * this currently sets the same rate for ADC and DAC, but limits | ||
656 | * ADC rate to 256X (96kHz). For 256X mode (96kHz), this sets ADC | ||
657 | * oversampling to 64x, as recommended by WM8776 datasheet. | ||
658 | * Setting the rate is not really necessary in slave mode. | ||
659 | */ | ||
660 | adc_ratio = ratio; | ||
661 | if (adc_ratio < WM8776_CLOCK_RATIO_256FS) | ||
662 | adc_ratio = WM8776_CLOCK_RATIO_256FS; | ||
663 | |||
664 | val = adc_ratio; | ||
665 | if (adc_ratio == WM8776_CLOCK_RATIO_256FS) | ||
666 | val |= 8; | ||
667 | val |= ratio << 4; | ||
668 | |||
669 | mutex_lock(&chip->mutex); | ||
670 | for (i = 0; i < 2; i++) | ||
671 | wm8776_write_bits(ice, &chip->wm[i], | ||
672 | WM8776_REG_MASTER_MODE_CONTROL, | ||
673 | 0x180, val); | ||
674 | mutex_unlock(&chip->mutex); | ||
675 | } | ||
676 | |||
677 | /* | ||
678 | * supported sample rates (to override the default one) | ||
679 | */ | ||
680 | |||
681 | static unsigned int rates[] = { | ||
682 | 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 | ||
683 | }; | ||
684 | |||
685 | /* playback rates: 32..192 kHz */ | ||
686 | static struct snd_pcm_hw_constraint_list dac_rates = { | ||
687 | .count = ARRAY_SIZE(rates), | ||
688 | .list = rates, | ||
689 | .mask = 0 | ||
690 | }; | ||
691 | |||
692 | |||
693 | /* | ||
694 | * chip addresses on I2C bus | ||
695 | */ | ||
696 | static unsigned char wm8776_addr[2] __devinitdata = { | ||
697 | 0x34, 0x36, /* codec 0 & 1 */ | ||
698 | }; | ||
699 | |||
700 | /* | ||
701 | * initialize the chip | ||
702 | */ | ||
703 | static int __devinit maya44_init(struct snd_ice1712 *ice) | ||
704 | { | ||
705 | int i; | ||
706 | struct snd_maya44 *chip; | ||
707 | |||
708 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
709 | if (!chip) | ||
710 | return -ENOMEM; | ||
711 | mutex_init(&chip->mutex); | ||
712 | chip->ice = ice; | ||
713 | ice->spec = chip; | ||
714 | |||
715 | /* initialise codecs */ | ||
716 | ice->num_total_dacs = 4; | ||
717 | ice->num_total_adcs = 4; | ||
718 | ice->akm_codecs = 0; | ||
719 | |||
720 | for (i = 0; i < 2; i++) { | ||
721 | wm8776_init(ice, &chip->wm[i], wm8776_addr[i]); | ||
722 | wm8776_select_input(chip, i, MAYA_LINE_IN); | ||
723 | } | ||
724 | |||
725 | /* set card specific rates */ | ||
726 | ice->hw_rates = &dac_rates; | ||
727 | |||
728 | /* register change rate notifier */ | ||
729 | ice->gpio.set_pro_rate = set_rate; | ||
730 | |||
731 | /* RDMA1 (2nd input channel) is used for ADC by default */ | ||
732 | ice->force_rdma1 = 1; | ||
733 | |||
734 | /* have an own routing control */ | ||
735 | ice->own_routing = 1; | ||
736 | |||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | |||
741 | /* | ||
742 | * Maya44 boards don't provide the EEPROM data except for the vendor IDs. | ||
743 | * hence the driver needs to sets up it properly. | ||
744 | */ | ||
745 | |||
746 | static unsigned char maya44_eeprom[] __devinitdata = { | ||
747 | [ICE_EEP2_SYSCONF] = 0x45, | ||
748 | /* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */ | ||
749 | [ICE_EEP2_ACLINK] = 0x80, | ||
750 | /* I2S */ | ||
751 | [ICE_EEP2_I2S] = 0xf8, | ||
752 | /* vol, 96k, 24bit, 192k */ | ||
753 | [ICE_EEP2_SPDIF] = 0xc3, | ||
754 | /* enable spdif out, spdif out supp, spdif-in, ext spdif out */ | ||
755 | [ICE_EEP2_GPIO_DIR] = 0xff, | ||
756 | [ICE_EEP2_GPIO_DIR1] = 0xff, | ||
757 | [ICE_EEP2_GPIO_DIR2] = 0xff, | ||
758 | [ICE_EEP2_GPIO_MASK] = 0/*0x9f*/, | ||
759 | [ICE_EEP2_GPIO_MASK1] = 0/*0xff*/, | ||
760 | [ICE_EEP2_GPIO_MASK2] = 0/*0x7f*/, | ||
761 | [ICE_EEP2_GPIO_STATE] = (1 << GPIO_PHANTOM_OFF) | | ||
762 | (1 << GPIO_SPDIF_IN_INV), | ||
763 | [ICE_EEP2_GPIO_STATE1] = 0x00, | ||
764 | [ICE_EEP2_GPIO_STATE2] = 0x00, | ||
765 | }; | ||
766 | |||
767 | /* entry point */ | ||
768 | struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = { | ||
769 | { | ||
770 | .subvendor = VT1724_SUBDEVICE_MAYA44, | ||
771 | .name = "ESI Maya44", | ||
772 | .model = "maya44", | ||
773 | .chip_init = maya44_init, | ||
774 | .build_controls = maya44_add_controls, | ||
775 | .eeprom_size = sizeof(maya44_eeprom), | ||
776 | .eeprom_data = maya44_eeprom, | ||
777 | }, | ||
778 | { } /* terminator */ | ||
779 | }; | ||
diff --git a/sound/pci/ice1712/maya44.h b/sound/pci/ice1712/maya44.h new file mode 100644 index 000000000000..eafd03a8f4b5 --- /dev/null +++ b/sound/pci/ice1712/maya44.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef __SOUND_MAYA44_H | ||
2 | #define __SOUND_MAYA44_H | ||
3 | |||
4 | #define MAYA44_DEVICE_DESC "{ESI,Maya44}," | ||
5 | |||
6 | #define VT1724_SUBDEVICE_MAYA44 0x34315441 /* Maya44 */ | ||
7 | |||
8 | extern struct snd_ice1712_card_info snd_vt1724_maya44_cards[]; | ||
9 | |||
10 | #endif /* __SOUND_MAYA44_H */ | ||