blob: 110d16e52733b5c10537c9addd7f555018bd78c5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * ALSA driver for ICEnsemble VT1724 (Envy24HT)
3 *
4 * Lowlevel functions for Terratec Aureon cards
5 *
6 * Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 * NOTES:
24 *
Takashi Iwaiab0c7d72005-11-17 15:00:18 +010025 * - we reuse the struct snd_akm4xxx record for storing the wm8770 codec data.
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 * both wm and akm codecs are pretty similar, so we can integrate
27 * both controls in the future, once if wm codecs are reused in
28 * many boards.
29 *
30 * - DAC digital volumes are not implemented in the mixer.
31 * if they show better response than DAC analog volumes, we can use them
32 * instead.
33 *
34 * Lowlevel functions for AudioTrak Prodigy 7.1 (and possibly 192) cards
35 * Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
36 *
37 * version 0.82: Stable / not all features work yet (no communication with AC97 secondary)
38 * added 64x/128x oversampling switch (should be 64x only for 96khz)
39 * fixed some recording labels (still need to check the rest)
40 * recording is working probably thanks to correct wm8770 initialization
41 *
42 * version 0.5: Initial release:
43 * working: analog output, mixer, headphone amplifier switch
44 * not working: prety much everything else, at least i could verify that
45 * we have no digital output, no capture, pretty bad clicks and poops
46 * on mixer switch and other coll stuff.
Alexander Beregalov1ce211a2008-09-07 01:19:00 +040047 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Alexander Beregalov1ce211a2008-09-07 01:19:00 +040049#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#include <linux/delay.h>
51#include <linux/interrupt.h>
52#include <linux/init.h>
53#include <linux/slab.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010054#include <linux/mutex.h>
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#include <sound/core.h>
57
58#include "ice1712.h"
59#include "envy24ht.h"
60#include "aureon.h"
Takashi Iwaif640c322006-08-30 16:57:37 +020061#include <sound/tlv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
Takashi Iwai7cda8ba2008-01-18 13:36:07 +010063/* AC97 register cache for Aureon */
64struct aureon_spec {
65 unsigned short stac9744[64];
66 unsigned int cs8415_mux;
67 unsigned short master[2];
68 unsigned short vol[8];
69 unsigned char pca9554_out;
70};
71
Linus Torvalds1da177e2005-04-16 15:20:36 -070072/* WM8770 registers */
73#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
74#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
75#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
76#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */
77#define WM_PHASE_SWAP 0x12 /* DAC phase */
78#define WM_DAC_CTRL1 0x13 /* DAC control bits */
79#define WM_MUTE 0x14 /* mute controls */
80#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
81#define WM_INT_CTRL 0x16 /* interface control */
82#define WM_MASTER 0x17 /* master clock and mode */
83#define WM_POWERDOWN 0x18 /* power-down controls */
84#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
85#define WM_ADC_MUX 0x1b /* input MUX */
86#define WM_OUT_MUX1 0x1c /* output MUX */
87#define WM_OUT_MUX2 0x1e /* output MUX */
88#define WM_RESET 0x1f /* software reset */
89
90/* CS8415A registers */
91#define CS8415_CTRL1 0x01
92#define CS8415_CTRL2 0x02
93#define CS8415_QSUB 0x14
94#define CS8415_RATIO 0x1E
95#define CS8415_C_BUFFER 0x20
96#define CS8415_ID 0x7F
97
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +020098/* PCA9554 registers */
99#define PCA9554_DEV 0x40 /* I2C device address */
100#define PCA9554_IN 0x00 /* input port */
101#define PCA9554_OUT 0x01 /* output port */
102#define PCA9554_INVERT 0x02 /* input invert */
103#define PCA9554_DIR 0x03 /* port directions */
104
105/*
106 * Aureon Universe additional controls using PCA9554
107 */
108
109/*
110 * Send data to pca9554
111 */
112static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
113 unsigned char data)
114{
115 unsigned int tmp;
116 int i, j;
117 unsigned char dev = PCA9554_DEV; /* ID 0100000, write */
118 unsigned char val = 0;
119
120 tmp = snd_ice1712_gpio_read(ice);
121
122 snd_ice1712_gpio_set_mask(ice, ~(AUREON_SPI_MOSI|AUREON_SPI_CLK|
123 AUREON_WM_RW|AUREON_WM_CS|
124 AUREON_CS8415_CS));
125 tmp |= AUREON_WM_RW;
126 tmp |= AUREON_CS8415_CS | AUREON_WM_CS; /* disable SPI devices */
127
128 tmp &= ~AUREON_SPI_MOSI;
129 tmp &= ~AUREON_SPI_CLK;
130 snd_ice1712_gpio_write(ice, tmp);
131 udelay(50);
132
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400133 /*
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200134 * send i2c stop condition and start condition
135 * to obtain sane state
136 */
137 tmp |= AUREON_SPI_CLK;
138 snd_ice1712_gpio_write(ice, tmp);
139 udelay(50);
140 tmp |= AUREON_SPI_MOSI;
141 snd_ice1712_gpio_write(ice, tmp);
142 udelay(100);
143 tmp &= ~AUREON_SPI_MOSI;
144 snd_ice1712_gpio_write(ice, tmp);
145 udelay(50);
146 tmp &= ~AUREON_SPI_CLK;
147 snd_ice1712_gpio_write(ice, tmp);
148 udelay(100);
149 /*
150 * send device address, command and value,
151 * skipping ack cycles inbetween
152 */
153 for (j = 0; j < 3; j++) {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400154 switch (j) {
155 case 0:
156 val = dev;
157 break;
158 case 1:
159 val = reg;
160 break;
161 case 2:
162 val = data;
163 break;
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200164 }
165 for (i = 7; i >= 0; i--) {
166 tmp &= ~AUREON_SPI_CLK;
167 snd_ice1712_gpio_write(ice, tmp);
168 udelay(40);
169 if (val & (1 << i))
170 tmp |= AUREON_SPI_MOSI;
171 else
172 tmp &= ~AUREON_SPI_MOSI;
173 snd_ice1712_gpio_write(ice, tmp);
174 udelay(40);
175 tmp |= AUREON_SPI_CLK;
176 snd_ice1712_gpio_write(ice, tmp);
177 udelay(40);
178 }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400179 tmp &= ~AUREON_SPI_CLK;
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200180 snd_ice1712_gpio_write(ice, tmp);
181 udelay(40);
182 tmp |= AUREON_SPI_CLK;
183 snd_ice1712_gpio_write(ice, tmp);
184 udelay(40);
185 tmp &= ~AUREON_SPI_CLK;
186 snd_ice1712_gpio_write(ice, tmp);
187 udelay(40);
188 }
189 tmp &= ~AUREON_SPI_CLK;
190 snd_ice1712_gpio_write(ice, tmp);
191 udelay(40);
192 tmp &= ~AUREON_SPI_MOSI;
193 snd_ice1712_gpio_write(ice, tmp);
194 udelay(40);
195 tmp |= AUREON_SPI_CLK;
196 snd_ice1712_gpio_write(ice, tmp);
197 udelay(50);
198 tmp |= AUREON_SPI_MOSI;
199 snd_ice1712_gpio_write(ice, tmp);
200 udelay(100);
201}
202
203static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
204 struct snd_ctl_elem_info *uinfo)
205{
206 char *texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"};
207
208 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
209 uinfo->count = 1;
210 uinfo->value.enumerated.items = 3;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400211 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200212 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
213 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
214 return 0;
215}
216
217static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
218 struct snd_ctl_elem_value *ucontrol)
219{
220 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100221 struct aureon_spec *spec = ice->spec;
222 ucontrol->value.enumerated.item[0] = spec->pca9554_out;
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200223 return 0;
224}
225
226static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
227 struct snd_ctl_elem_value *ucontrol)
228{
229 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100230 struct aureon_spec *spec = ice->spec;
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200231 unsigned char oval, nval;
232 int change;
233
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100234 nval = ucontrol->value.enumerated.item[0];
235 if (nval >= 3)
236 return -EINVAL;
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200237 snd_ice1712_save_gpio_status(ice);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100238 oval = spec->pca9554_out;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400239 change = (oval != nval);
240 if (change) {
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200241 aureon_pca9554_write(ice, PCA9554_OUT, nval);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100242 spec->pca9554_out = nval;
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200243 }
244 snd_ice1712_restore_gpio_status(ice);
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200245 return change;
246}
247
248
249static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
250 unsigned short val)
251{
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100252 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 unsigned int tmp;
254
255 /* Send address to XILINX chip */
256 tmp = (snd_ice1712_gpio_read(ice) & ~0xFF) | (reg & 0x7F);
257 snd_ice1712_gpio_write(ice, tmp);
258 udelay(10);
259 tmp |= AUREON_AC97_ADDR;
260 snd_ice1712_gpio_write(ice, tmp);
261 udelay(10);
262 tmp &= ~AUREON_AC97_ADDR;
263 snd_ice1712_gpio_write(ice, tmp);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400264 udelay(10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
266 /* Send low-order byte to XILINX chip */
267 tmp &= ~AUREON_AC97_DATA_MASK;
268 tmp |= val & AUREON_AC97_DATA_MASK;
269 snd_ice1712_gpio_write(ice, tmp);
270 udelay(10);
271 tmp |= AUREON_AC97_DATA_LOW;
272 snd_ice1712_gpio_write(ice, tmp);
273 udelay(10);
274 tmp &= ~AUREON_AC97_DATA_LOW;
275 snd_ice1712_gpio_write(ice, tmp);
276 udelay(10);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 /* Send high-order byte to XILINX chip */
279 tmp &= ~AUREON_AC97_DATA_MASK;
280 tmp |= (val >> 8) & AUREON_AC97_DATA_MASK;
281
282 snd_ice1712_gpio_write(ice, tmp);
283 udelay(10);
284 tmp |= AUREON_AC97_DATA_HIGH;
285 snd_ice1712_gpio_write(ice, tmp);
286 udelay(10);
287 tmp &= ~AUREON_AC97_DATA_HIGH;
288 snd_ice1712_gpio_write(ice, tmp);
289 udelay(10);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 /* Instruct XILINX chip to parse the data to the STAC9744 chip */
292 tmp |= AUREON_AC97_COMMIT;
293 snd_ice1712_gpio_write(ice, tmp);
294 udelay(10);
295 tmp &= ~AUREON_AC97_COMMIT;
296 snd_ice1712_gpio_write(ice, tmp);
297 udelay(10);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400298
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 /* Store the data in out private buffer */
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100300 spec->stac9744[(reg & 0x7F) >> 1] = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301}
302
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100303static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100305 struct aureon_spec *spec = ice->spec;
306 return spec->stac9744[(reg & 0x7F) >> 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309/*
310 * Initialize STAC9744 chip
311 */
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400312static int aureon_ac97_init(struct snd_ice1712 *ice)
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +0200313{
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100314 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 int i;
Takashi Iwai32b47da2007-01-29 15:26:36 +0100316 static const unsigned short ac97_defaults[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 0x00, 0x9640,
318 0x02, 0x8000,
319 0x04, 0x8000,
320 0x06, 0x8000,
321 0x0C, 0x8008,
322 0x0E, 0x8008,
323 0x10, 0x8808,
324 0x12, 0x8808,
325 0x14, 0x8808,
326 0x16, 0x8808,
327 0x18, 0x8808,
328 0x1C, 0x8000,
329 0x26, 0x000F,
330 0x28, 0x0201,
331 0x2C, 0xBB80,
332 0x32, 0xBB80,
333 0x7C, 0x8384,
334 0x7E, 0x7644,
335 (unsigned short)-1
336 };
337 unsigned int tmp;
338
339 /* Cold reset */
340 tmp = (snd_ice1712_gpio_read(ice) | AUREON_AC97_RESET) & ~AUREON_AC97_DATA_MASK;
341 snd_ice1712_gpio_write(ice, tmp);
342 udelay(3);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400343
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 tmp &= ~AUREON_AC97_RESET;
345 snd_ice1712_gpio_write(ice, tmp);
346 udelay(3);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 tmp |= AUREON_AC97_RESET;
349 snd_ice1712_gpio_write(ice, tmp);
350 udelay(3);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400351
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100352 memset(&spec->stac9744, 0, sizeof(spec->stac9744));
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400353 for (i = 0; ac97_defaults[i] != (unsigned short)-1; i += 2)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100354 spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400355
356 /* Unmute AC'97 master volume permanently - muting is done by WM8770 */
357 aureon_ac97_write(ice, AC97_MASTER, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
359 return 0;
360}
361
362#define AUREON_AC97_STEREO 0x80
363
364/*
365 * AC'97 volume controls
366 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100367static int aureon_ac97_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368{
369 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
370 uinfo->count = kcontrol->private_value & AUREON_AC97_STEREO ? 2 : 1;
371 uinfo->value.integer.min = 0;
372 uinfo->value.integer.max = 31;
373 return 0;
374}
375
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100376static int aureon_ac97_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100378 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 unsigned short vol;
380
Ingo Molnar62932df2006-01-16 16:34:20 +0100381 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383 vol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
384 ucontrol->value.integer.value[0] = 0x1F - (vol & 0x1F);
385 if (kcontrol->private_value & AUREON_AC97_STEREO)
386 ucontrol->value.integer.value[1] = 0x1F - ((vol >> 8) & 0x1F);
387
Ingo Molnar62932df2006-01-16 16:34:20 +0100388 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 return 0;
390}
391
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100392static int aureon_ac97_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100394 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 unsigned short ovol, nvol;
396 int change;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 snd_ice1712_save_gpio_status(ice);
399
400 ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
401 nvol = (0x1F - ucontrol->value.integer.value[0]) & 0x001F;
402 if (kcontrol->private_value & AUREON_AC97_STEREO)
403 nvol |= ((0x1F - ucontrol->value.integer.value[1]) << 8) & 0x1F00;
404 nvol |= ovol & ~0x1F1F;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400405
406 change = (ovol != nvol);
407 if (change)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
409
410 snd_ice1712_restore_gpio_status(ice);
411
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400412 return change;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413}
414
415/*
416 * AC'97 mute controls
417 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200418#define aureon_ac97_mute_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100420static int aureon_ac97_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100422 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
Ingo Molnar62932df2006-01-16 16:34:20 +0100424 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400426 ucontrol->value.integer.value[0] = aureon_ac97_read(ice,
427 kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
Ingo Molnar62932df2006-01-16 16:34:20 +0100429 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 return 0;
431}
432
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100433static int aureon_ac97_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100435 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 unsigned short ovol, nvol;
437 int change;
438
439 snd_ice1712_save_gpio_status(ice);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400440
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400442 nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~0x8000);
443
444 change = (ovol != nvol);
445 if (change)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 snd_ice1712_restore_gpio_status(ice);
449
450 return change;
451}
452
453/*
454 * AC'97 mute controls
455 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200456#define aureon_ac97_micboost_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100458static int aureon_ac97_micboost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100460 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Ingo Molnar62932df2006-01-16 16:34:20 +0100462 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
464 ucontrol->value.integer.value[0] = aureon_ac97_read(ice, AC97_MIC) & 0x0020 ? 0 : 1;
465
Ingo Molnar62932df2006-01-16 16:34:20 +0100466 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 return 0;
468}
469
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100470static int aureon_ac97_micboost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100472 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 unsigned short ovol, nvol;
474 int change;
475
476 snd_ice1712_save_gpio_status(ice);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 ovol = aureon_ac97_read(ice, AC97_MIC);
479 nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x0020) | (ovol & ~0x0020);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400480
481 change = (ovol != nvol);
482 if (change)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 aureon_ac97_write(ice, AC97_MIC, nvol);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 snd_ice1712_restore_gpio_status(ice);
486
487 return change;
488}
489
490/*
491 * write data in the SPI mode
492 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100493static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
495 unsigned int tmp;
496 int i;
Takashi Iwai45fe7222006-01-13 13:50:16 +0100497 unsigned int mosi, clk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 tmp = snd_ice1712_gpio_read(ice);
500
Toshimune Konnocdf88ef2006-12-18 13:12:18 +0100501 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
502 ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) {
Takashi Iwai45fe7222006-01-13 13:50:16 +0100503 snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_SPI_MOSI|PRODIGY_SPI_CLK|PRODIGY_WM_CS));
504 mosi = PRODIGY_SPI_MOSI;
505 clk = PRODIGY_SPI_CLK;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400506 } else {
Takashi Iwai45fe7222006-01-13 13:50:16 +0100507 snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RW|AUREON_SPI_MOSI|AUREON_SPI_CLK|
508 AUREON_WM_CS|AUREON_CS8415_CS));
509 mosi = AUREON_SPI_MOSI;
510 clk = AUREON_SPI_CLK;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400511
Takashi Iwai45fe7222006-01-13 13:50:16 +0100512 tmp |= AUREON_WM_RW;
513 }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400514
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 tmp &= ~cs;
516 snd_ice1712_gpio_write(ice, tmp);
517 udelay(1);
518
519 for (i = bits - 1; i >= 0; i--) {
Takashi Iwai45fe7222006-01-13 13:50:16 +0100520 tmp &= ~clk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 snd_ice1712_gpio_write(ice, tmp);
522 udelay(1);
523 if (data & (1 << i))
Takashi Iwai45fe7222006-01-13 13:50:16 +0100524 tmp |= mosi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 else
Takashi Iwai45fe7222006-01-13 13:50:16 +0100526 tmp &= ~mosi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 snd_ice1712_gpio_write(ice, tmp);
528 udelay(1);
Takashi Iwai45fe7222006-01-13 13:50:16 +0100529 tmp |= clk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 snd_ice1712_gpio_write(ice, tmp);
531 udelay(1);
532 }
533
Takashi Iwai45fe7222006-01-13 13:50:16 +0100534 tmp &= ~clk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 tmp |= cs;
536 snd_ice1712_gpio_write(ice, tmp);
537 udelay(1);
Takashi Iwai45fe7222006-01-13 13:50:16 +0100538 tmp |= clk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 snd_ice1712_gpio_write(ice, tmp);
540 udelay(1);
541}
542
543/*
544 * Read data in SPI mode
545 */
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400546static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs,
547 unsigned int data, int bits, unsigned char *buffer, int size)
548{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 int i, j;
550 unsigned int tmp;
551
552 tmp = (snd_ice1712_gpio_read(ice) & ~AUREON_SPI_CLK) | AUREON_CS8415_CS|AUREON_WM_CS;
553 snd_ice1712_gpio_write(ice, tmp);
554 tmp &= ~cs;
555 snd_ice1712_gpio_write(ice, tmp);
556 udelay(1);
557
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400558 for (i = bits-1; i >= 0; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 if (data & (1 << i))
560 tmp |= AUREON_SPI_MOSI;
561 else
562 tmp &= ~AUREON_SPI_MOSI;
563 snd_ice1712_gpio_write(ice, tmp);
564 udelay(1);
565
566 tmp |= AUREON_SPI_CLK;
567 snd_ice1712_gpio_write(ice, tmp);
568 udelay(1);
569
570 tmp &= ~AUREON_SPI_CLK;
571 snd_ice1712_gpio_write(ice, tmp);
572 udelay(1);
573 }
574
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400575 for (j = 0; j < size; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 unsigned char outdata = 0;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400577 for (i = 7; i >= 0; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 tmp = snd_ice1712_gpio_read(ice);
579 outdata <<= 1;
580 outdata |= (tmp & AUREON_SPI_MISO) ? 1 : 0;
581 udelay(1);
582
583 tmp |= AUREON_SPI_CLK;
584 snd_ice1712_gpio_write(ice, tmp);
585 udelay(1);
586
587 tmp &= ~AUREON_SPI_CLK;
588 snd_ice1712_gpio_write(ice, tmp);
589 udelay(1);
590 }
591 buffer[j] = outdata;
592 }
593
594 tmp |= cs;
595 snd_ice1712_gpio_write(ice, tmp);
596}
597
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400598static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg)
599{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 unsigned char val;
601 aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
602 aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, &val, 1);
603 return val;
604}
605
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400606static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg,
607 unsigned char *buffer, int size)
608{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
610 aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, buffer, size);
611}
612
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400613static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg,
614 unsigned char val)
615{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 aureon_spi_write(ice, AUREON_CS8415_CS, 0x200000 | (reg << 8) | val, 24);
617}
618
619/*
620 * get the current register value of WM codec
621 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100622static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623{
624 reg <<= 1;
625 return ((unsigned short)ice->akm[0].images[reg] << 8) |
626 ice->akm[0].images[reg + 1];
627}
628
629/*
630 * set the register value of WM codec
631 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100632static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
Takashi Iwai45fe7222006-01-13 13:50:16 +0100634 aureon_spi_write(ice,
Toshimune Konnocdf88ef2006-12-18 13:12:18 +0100635 ((ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
636 ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) ?
637 PRODIGY_WM_CS : AUREON_WM_CS),
Takashi Iwai45fe7222006-01-13 13:50:16 +0100638 (reg << 9) | (val & 0x1ff), 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639}
640
641/*
642 * set the register value of WM codec and remember it
643 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100644static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
646 wm_put_nocache(ice, reg, val);
647 reg <<= 1;
648 ice->akm[0].images[reg] = val >> 8;
649 ice->akm[0].images[reg + 1] = val;
650}
651
652/*
653 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200654#define aureon_mono_bool_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
656/*
657 * AC'97 master playback mute controls (Mute on WM8770 chip)
658 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200659#define aureon_ac97_mmute_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100661static int aureon_ac97_mmute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100663 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Ingo Molnar62932df2006-01-16 16:34:20 +0100665 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX1) >> 1) & 0x01;
668
Ingo Molnar62932df2006-01-16 16:34:20 +0100669 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 return 0;
671}
672
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400673static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
674{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100675 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 unsigned short ovol, nvol;
677 int change;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 snd_ice1712_save_gpio_status(ice);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 ovol = wm_get(ice, WM_OUT_MUX1);
682 nvol = (ovol & ~0x02) | (ucontrol->value.integer.value[0] ? 0x02 : 0x00);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400683 change = (ovol != nvol);
684 if (change)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 wm_put(ice, WM_OUT_MUX1, nvol);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400686
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 snd_ice1712_restore_gpio_status(ice);
688
689 return change;
690}
691
Takashi Iwai0cb29ea2007-01-29 15:33:49 +0100692static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
693static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
694static const DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
695static const DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
696static const DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
Takashi Iwaif640c322006-08-30 16:57:37 +0200697
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698/*
699 * Logarithmic volume values for WM8770
700 * Computed as 20 * Log10(255 / x)
701 */
Takashi Iwai32b47da2007-01-29 15:26:36 +0100702static const unsigned char wm_vol[256] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
704 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
705 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
706 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
707 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
708 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
709 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
710 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
711 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
712 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
713 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
714 0, 0
715};
716
717#define WM_VOL_MAX (sizeof(wm_vol) - 1)
718#define WM_VOL_MUTE 0x8000
719
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100720static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721{
722 unsigned char nvol;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
725 nvol = 0;
726 else
727 nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400728
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 wm_put(ice, index, nvol);
730 wm_put_nocache(ice, index, 0x180 | nvol);
731}
732
733/*
734 * DAC mute control
735 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200736#define wm_pcm_mute_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100738static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100740 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Ingo Molnar62932df2006-01-16 16:34:20 +0100742 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
Ingo Molnar62932df2006-01-16 16:34:20 +0100744 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 return 0;
746}
747
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100748static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100750 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 unsigned short nval, oval;
752 int change;
753
754 snd_ice1712_save_gpio_status(ice);
755 oval = wm_get(ice, WM_MUTE);
756 nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400757 change = (oval != nval);
758 if (change)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 wm_put(ice, WM_MUTE, nval);
760 snd_ice1712_restore_gpio_status(ice);
761
762 return change;
763}
764
765/*
766 * Master volume attenuation mixer control
767 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100768static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769{
770 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
771 uinfo->count = 2;
772 uinfo->value.integer.min = 0;
773 uinfo->value.integer.max = WM_VOL_MAX;
774 return 0;
775}
776
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100777static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100779 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100780 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 int i;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400782 for (i = 0; i < 2; i++)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100783 ucontrol->value.integer.value[i] =
784 spec->master[i] & ~WM_VOL_MUTE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 return 0;
786}
787
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100788static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100790 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100791 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 int ch, change = 0;
793
794 snd_ice1712_save_gpio_status(ice);
795 for (ch = 0; ch < 2; ch++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100796 unsigned int vol = ucontrol->value.integer.value[ch];
797 if (vol > WM_VOL_MAX)
798 continue;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100799 vol |= spec->master[ch] & WM_VOL_MUTE;
800 if (vol != spec->master[ch]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 int dac;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100802 spec->master[ch] = vol;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
804 wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100805 spec->vol[dac + ch],
806 spec->master[ch]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 change = 1;
808 }
809 }
810 snd_ice1712_restore_gpio_status(ice);
811 return change;
812}
813
814/*
815 * DAC volume attenuation mixer control
816 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100817static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
819 int voices = kcontrol->private_value >> 8;
820 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
821 uinfo->count = voices;
822 uinfo->value.integer.min = 0; /* mute (-101dB) */
823 uinfo->value.integer.max = 0x7F; /* 0dB */
824 return 0;
825}
826
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100827static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100829 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100830 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 int i, ofs, voices;
832
833 voices = kcontrol->private_value >> 8;
834 ofs = kcontrol->private_value & 0xff;
835 for (i = 0; i < voices; i++)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100836 ucontrol->value.integer.value[i] =
837 spec->vol[ofs+i] & ~WM_VOL_MUTE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 return 0;
839}
840
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100841static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100843 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100844 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 int i, idx, ofs, voices;
846 int change = 0;
847
848 voices = kcontrol->private_value >> 8;
849 ofs = kcontrol->private_value & 0xff;
850 snd_ice1712_save_gpio_status(ice);
851 for (i = 0; i < voices; i++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100852 unsigned int vol = ucontrol->value.integer.value[i];
853 if (vol > 0x7f)
854 continue;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100855 vol |= spec->vol[ofs+i];
856 if (vol != spec->vol[ofs+i]) {
857 spec->vol[ofs+i] = vol;
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100858 idx = WM_DAC_ATTEN + ofs + i;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100859 wm_set_vol(ice, idx, spec->vol[ofs + i],
860 spec->master[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 change = 1;
862 }
863 }
864 snd_ice1712_restore_gpio_status(ice);
865 return change;
866}
867
868/*
869 * WM8770 mute control
870 */
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400871static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
872{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
874 uinfo->count = kcontrol->private_value >> 8;
875 uinfo->value.integer.min = 0;
876 uinfo->value.integer.max = 1;
877 return 0;
878}
879
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100880static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100882 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100883 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 int voices, ofs, i;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400885
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 voices = kcontrol->private_value >> 8;
887 ofs = kcontrol->private_value & 0xFF;
888
889 for (i = 0; i < voices; i++)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100890 ucontrol->value.integer.value[i] =
891 (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 return 0;
893}
894
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100895static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100897 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100898 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 int change = 0, voices, ofs, i;
900
901 voices = kcontrol->private_value >> 8;
902 ofs = kcontrol->private_value & 0xFF;
903
904 snd_ice1712_save_gpio_status(ice);
905 for (i = 0; i < voices; i++) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100906 int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 if (ucontrol->value.integer.value[i] != val) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100908 spec->vol[ofs + i] &= ~WM_VOL_MUTE;
909 spec->vol[ofs + i] |=
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100911 wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
912 spec->master[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 change = 1;
914 }
915 }
916 snd_ice1712_restore_gpio_status(ice);
917
918 return change;
919}
920
921/*
922 * WM8770 master mute control
923 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200924#define wm_master_mute_info snd_ctl_boolean_stereo_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100926static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100928 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100929 struct aureon_spec *spec = ice->spec;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +0400930
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100931 ucontrol->value.integer.value[0] =
932 (spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
933 ucontrol->value.integer.value[1] =
934 (spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 return 0;
936}
937
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100938static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100940 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100941 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 int change = 0, i;
943
944 snd_ice1712_save_gpio_status(ice);
945 for (i = 0; i < 2; i++) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100946 int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 if (ucontrol->value.integer.value[i] != val) {
948 int dac;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100949 spec->master[i] &= ~WM_VOL_MUTE;
950 spec->master[i] |=
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
952 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
953 wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100954 spec->vol[dac + i],
955 spec->master[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 change = 1;
957 }
958 }
959 snd_ice1712_restore_gpio_status(ice);
960
961 return change;
962}
963
964/* digital master volume */
965#define PCM_0dB 0xff
966#define PCM_RES 128 /* -64dB */
967#define PCM_MIN (PCM_0dB - PCM_RES)
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100968static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969{
970 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
971 uinfo->count = 1;
972 uinfo->value.integer.min = 0; /* mute (-64dB) */
973 uinfo->value.integer.max = PCM_RES; /* 0dB */
974 return 0;
975}
976
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100977static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100979 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 unsigned short val;
981
Ingo Molnar62932df2006-01-16 16:34:20 +0100982 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
984 val = val > PCM_MIN ? (val - PCM_MIN) : 0;
985 ucontrol->value.integer.value[0] = val;
Ingo Molnar62932df2006-01-16 16:34:20 +0100986 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 return 0;
988}
989
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100990static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100992 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 unsigned short ovol, nvol;
994 int change = 0;
995
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 nvol = ucontrol->value.integer.value[0];
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100997 if (nvol > PCM_RES)
998 return -EINVAL;
999 snd_ice1712_save_gpio_status(ice);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
1001 ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
1002 if (ovol != nvol) {
1003 wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
1004 wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
1005 change = 1;
1006 }
1007 snd_ice1712_restore_gpio_status(ice);
1008 return change;
1009}
1010
1011/*
1012 * ADC mute control
1013 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +02001014#define wm_adc_mute_info snd_ctl_boolean_stereo_info
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001016static int wm_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001018 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 unsigned short val;
1020 int i;
1021
Ingo Molnar62932df2006-01-16 16:34:20 +01001022 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 for (i = 0; i < 2; i++) {
1024 val = wm_get(ice, WM_ADC_GAIN + i);
1025 ucontrol->value.integer.value[i] = ~val>>5 & 0x1;
1026 }
Ingo Molnar62932df2006-01-16 16:34:20 +01001027 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return 0;
1029}
1030
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001031static int wm_adc_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001033 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 unsigned short new, old;
1035 int i, change = 0;
1036
1037 snd_ice1712_save_gpio_status(ice);
1038 for (i = 0; i < 2; i++) {
1039 old = wm_get(ice, WM_ADC_GAIN + i);
1040 new = (~ucontrol->value.integer.value[i]<<5&0x20) | (old&~0x20);
1041 if (new != old) {
1042 wm_put(ice, WM_ADC_GAIN + i, new);
1043 change = 1;
1044 }
1045 }
1046 snd_ice1712_restore_gpio_status(ice);
1047
1048 return change;
1049}
1050
1051/*
1052 * ADC gain mixer control
1053 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001054static int wm_adc_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055{
1056 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1057 uinfo->count = 2;
1058 uinfo->value.integer.min = 0; /* -12dB */
1059 uinfo->value.integer.max = 0x1f; /* 19dB */
1060 return 0;
1061}
1062
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001063static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001065 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 int i, idx;
1067 unsigned short vol;
1068
Ingo Molnar62932df2006-01-16 16:34:20 +01001069 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 for (i = 0; i < 2; i++) {
1071 idx = WM_ADC_GAIN + i;
1072 vol = wm_get(ice, idx) & 0x1f;
1073 ucontrol->value.integer.value[i] = vol;
1074 }
Ingo Molnar62932df2006-01-16 16:34:20 +01001075 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 return 0;
1077}
1078
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001079static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001081 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 int i, idx;
1083 unsigned short ovol, nvol;
1084 int change = 0;
1085
1086 snd_ice1712_save_gpio_status(ice);
1087 for (i = 0; i < 2; i++) {
1088 idx = WM_ADC_GAIN + i;
Takashi Iwai9cd17cd2007-11-15 15:56:07 +01001089 nvol = ucontrol->value.integer.value[i] & 0x1f;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 ovol = wm_get(ice, idx);
1091 if ((ovol & 0x1f) != nvol) {
1092 wm_put(ice, idx, nvol | (ovol & ~0x1f));
1093 change = 1;
1094 }
1095 }
1096 snd_ice1712_restore_gpio_status(ice);
1097 return change;
1098}
1099
1100/*
1101 * ADC input mux mixer control
1102 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001103static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104{
Takashi Iwai32b47da2007-01-29 15:26:36 +01001105 static const char * const texts[] = {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001106 "CD", /* AIN1 */
1107 "Aux", /* AIN2 */
1108 "Line", /* AIN3 */
1109 "Mic", /* AIN4 */
1110 "AC97" /* AIN5 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 };
Takashi Iwai32b47da2007-01-29 15:26:36 +01001112 static const char * const universe_texts[] = {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001113 "Aux1", /* AIN1 */
1114 "CD", /* AIN2 */
1115 "Phono", /* AIN3 */
1116 "Line", /* AIN4 */
1117 "Aux2", /* AIN5 */
1118 "Mic", /* AIN6 */
1119 "Aux3", /* AIN7 */
1120 "AC97" /* AIN8 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 };
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001122 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
1124 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1125 uinfo->count = 2;
1126 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
1127 uinfo->value.enumerated.items = 8;
1128 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
1129 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
1130 strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001131 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 uinfo->value.enumerated.items = 5;
1133 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
1134 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
1135 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1136 }
1137 return 0;
1138}
1139
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001140static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001142 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 unsigned short val;
1144
Ingo Molnar62932df2006-01-16 16:34:20 +01001145 mutex_lock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 val = wm_get(ice, WM_ADC_MUX);
Takashi Iwai66820252006-03-20 18:31:57 +01001147 ucontrol->value.enumerated.item[0] = val & 7;
1148 ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
Ingo Molnar62932df2006-01-16 16:34:20 +01001149 mutex_unlock(&ice->gpio_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 return 0;
1151}
1152
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001153static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001155 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 unsigned short oval, nval;
1157 int change;
1158
1159 snd_ice1712_save_gpio_status(ice);
1160 oval = wm_get(ice, WM_ADC_MUX);
1161 nval = oval & ~0x77;
Takashi Iwai66820252006-03-20 18:31:57 +01001162 nval |= ucontrol->value.enumerated.item[0] & 7;
1163 nval |= (ucontrol->value.enumerated.item[1] & 7) << 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 change = (oval != nval);
1165 if (change)
1166 wm_put(ice, WM_ADC_MUX, nval);
1167 snd_ice1712_restore_gpio_status(ice);
Takashi Iwai63786d02005-11-04 13:58:11 +01001168 return change;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169}
1170
1171/*
1172 * CS8415 Input mux
1173 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001174static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001176 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai32b47da2007-01-29 15:26:36 +01001177 static const char * const aureon_texts[] = {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001178 "CD", /* RXP0 */
1179 "Optical" /* RXP1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 };
Takashi Iwai32b47da2007-01-29 15:26:36 +01001181 static const char * const prodigy_texts[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 "CD",
1183 "Coax"
1184 };
1185 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1186 uinfo->count = 1;
1187 uinfo->value.enumerated.items = 2;
1188 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
1189 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
1190 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
1191 strcpy(uinfo->value.enumerated.name, prodigy_texts[uinfo->value.enumerated.item]);
1192 else
1193 strcpy(uinfo->value.enumerated.name, aureon_texts[uinfo->value.enumerated.item]);
1194 return 0;
1195}
1196
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001197static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001199 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01001200 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001202 /* snd_ice1712_save_gpio_status(ice); */
1203 /* val = aureon_cs8415_get(ice, CS8415_CTRL2); */
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01001204 ucontrol->value.enumerated.item[0] = spec->cs8415_mux;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001205 /* snd_ice1712_restore_gpio_status(ice); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 return 0;
1207}
1208
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001209static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001211 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01001212 struct aureon_spec *spec = ice->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 unsigned short oval, nval;
1214 int change;
1215
1216 snd_ice1712_save_gpio_status(ice);
1217 oval = aureon_cs8415_get(ice, CS8415_CTRL2);
1218 nval = oval & ~0x07;
Takashi Iwai66820252006-03-20 18:31:57 +01001219 nval |= ucontrol->value.enumerated.item[0] & 7;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 change = (oval != nval);
1221 if (change)
1222 aureon_cs8415_put(ice, CS8415_CTRL2, nval);
1223 snd_ice1712_restore_gpio_status(ice);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01001224 spec->cs8415_mux = ucontrol->value.enumerated.item[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 return change;
1226}
1227
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001228static int aureon_cs8415_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229{
1230 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1231 uinfo->count = 1;
1232 uinfo->value.integer.min = 0;
1233 uinfo->value.integer.max = 192000;
1234 return 0;
1235}
1236
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001237static int aureon_cs8415_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001239 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 unsigned char ratio;
1241 ratio = aureon_cs8415_get(ice, CS8415_RATIO);
1242 ucontrol->value.integer.value[0] = (int)((unsigned int)ratio * 750);
1243 return 0;
1244}
1245
1246/*
1247 * CS8415A Mute
1248 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +02001249#define aureon_cs8415_mute_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001251static int aureon_cs8415_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001253 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 snd_ice1712_save_gpio_status(ice);
1255 ucontrol->value.integer.value[0] = (aureon_cs8415_get(ice, CS8415_CTRL1) & 0x20) ? 0 : 1;
1256 snd_ice1712_restore_gpio_status(ice);
1257 return 0;
1258}
1259
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001260static int aureon_cs8415_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001262 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 unsigned char oval, nval;
1264 int change;
1265 snd_ice1712_save_gpio_status(ice);
1266 oval = aureon_cs8415_get(ice, CS8415_CTRL1);
1267 if (ucontrol->value.integer.value[0])
1268 nval = oval & ~0x20;
1269 else
1270 nval = oval | 0x20;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001271 change = (oval != nval);
1272 if (change)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 aureon_cs8415_put(ice, CS8415_CTRL1, nval);
1274 snd_ice1712_restore_gpio_status(ice);
1275 return change;
1276}
1277
1278/*
1279 * CS8415A Q-Sub info
1280 */
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001281static int aureon_cs8415_qsub_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1282{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
1284 uinfo->count = 10;
1285 return 0;
1286}
1287
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001288static int aureon_cs8415_qsub_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1289{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001290 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001291
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 snd_ice1712_save_gpio_status(ice);
1293 aureon_cs8415_read(ice, CS8415_QSUB, ucontrol->value.bytes.data, 10);
1294 snd_ice1712_restore_gpio_status(ice);
1295
1296 return 0;
1297}
1298
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001299static int aureon_cs8415_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1300{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
1302 uinfo->count = 1;
1303 return 0;
1304}
1305
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001306static int aureon_cs8415_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1307{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 memset(ucontrol->value.iec958.status, 0xFF, 24);
1309 return 0;
1310}
1311
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001312static int aureon_cs8415_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1313{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001314 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
1316 snd_ice1712_save_gpio_status(ice);
1317 aureon_cs8415_read(ice, CS8415_C_BUFFER, ucontrol->value.iec958.status, 24);
1318 snd_ice1712_restore_gpio_status(ice);
1319 return 0;
1320}
1321
1322/*
1323 * Headphone Amplifier
1324 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001325static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326{
1327 unsigned int tmp, tmp2;
1328
1329 tmp2 = tmp = snd_ice1712_gpio_read(ice);
1330 if (enable)
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01001331 if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
1332 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT)
Takashi Iwaic5130272006-05-23 15:46:10 +02001333 tmp |= AUREON_HP_SEL;
1334 else
1335 tmp |= PRODIGY_HP_SEL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 else
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01001337 if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
1338 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT)
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001339 tmp &= ~AUREON_HP_SEL;
Takashi Iwaic5130272006-05-23 15:46:10 +02001340 else
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001341 tmp &= ~PRODIGY_HP_SEL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if (tmp != tmp2) {
1343 snd_ice1712_gpio_write(ice, tmp);
1344 return 1;
1345 }
1346 return 0;
1347}
1348
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001349static int aureon_get_headphone_amp(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
1351 unsigned int tmp = snd_ice1712_gpio_read(ice);
1352
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001353 return (tmp & AUREON_HP_SEL) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355
Takashi Iwaia5ce8892007-07-23 15:42:26 +02001356#define aureon_hpamp_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001358static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001360 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
1363 return 0;
1364}
1365
1366
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001367static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001369 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001371 return aureon_set_headphone_amp(ice, ucontrol->value.integer.value[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372}
1373
1374/*
1375 * Deemphasis
1376 */
1377
Takashi Iwaia5ce8892007-07-23 15:42:26 +02001378#define aureon_deemp_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001380static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001382 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
1384 return 0;
1385}
1386
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001387static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001389 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 int temp, temp2;
1391 temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
1392 if (ucontrol->value.integer.value[0])
1393 temp |= 0xf;
1394 else
1395 temp &= ~0xf;
1396 if (temp != temp2) {
1397 wm_put(ice, WM_DAC_CTRL2, temp);
1398 return 1;
1399 }
1400 return 0;
1401}
1402
1403/*
1404 * ADC Oversampling
1405 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001406static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407{
Takashi Iwai32b47da2007-01-29 15:26:36 +01001408 static const char * const texts[2] = { "128x", "64x" };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
1410 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1411 uinfo->count = 1;
1412 uinfo->value.enumerated.items = 2;
1413
1414 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
1415 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
1416 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1417
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001418 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419}
1420
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001421static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001423 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
1425 return 0;
1426}
1427
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001428static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429{
1430 int temp, temp2;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001431 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
1433 temp2 = temp = wm_get(ice, WM_MASTER);
1434
1435 if (ucontrol->value.enumerated.item[0])
1436 temp |= 0x8;
1437 else
1438 temp &= ~0x8;
1439
1440 if (temp != temp2) {
1441 wm_put(ice, WM_MASTER, temp);
1442 return 1;
1443 }
1444 return 0;
1445}
1446
1447/*
1448 * mixers
1449 */
1450
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001451static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 {
1453 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1454 .name = "Master Playback Switch",
1455 .info = wm_master_mute_info,
1456 .get = wm_master_mute_get,
1457 .put = wm_master_mute_put
1458 },
1459 {
1460 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001461 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001462 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 .name = "Master Playback Volume",
1464 .info = wm_master_vol_info,
1465 .get = wm_master_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +02001466 .put = wm_master_vol_put,
1467 .tlv = { .p = db_scale_wm_dac }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 },
1469 {
1470 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1471 .name = "Front Playback Switch",
1472 .info = wm_mute_info,
1473 .get = wm_mute_get,
1474 .put = wm_mute_put,
1475 .private_value = (2 << 8) | 0
1476 },
1477 {
1478 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001479 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001480 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 .name = "Front Playback Volume",
1482 .info = wm_vol_info,
1483 .get = wm_vol_get,
1484 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +02001485 .private_value = (2 << 8) | 0,
1486 .tlv = { .p = db_scale_wm_dac }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 },
1488 {
1489 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1490 .name = "Rear Playback Switch",
1491 .info = wm_mute_info,
1492 .get = wm_mute_get,
1493 .put = wm_mute_put,
1494 .private_value = (2 << 8) | 2
1495 },
1496 {
1497 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001498 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001499 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 .name = "Rear Playback Volume",
1501 .info = wm_vol_info,
1502 .get = wm_vol_get,
1503 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +02001504 .private_value = (2 << 8) | 2,
1505 .tlv = { .p = db_scale_wm_dac }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 },
1507 {
1508 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1509 .name = "Center Playback Switch",
1510 .info = wm_mute_info,
1511 .get = wm_mute_get,
1512 .put = wm_mute_put,
1513 .private_value = (1 << 8) | 4
1514 },
1515 {
1516 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001517 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001518 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 .name = "Center Playback Volume",
1520 .info = wm_vol_info,
1521 .get = wm_vol_get,
1522 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +02001523 .private_value = (1 << 8) | 4,
1524 .tlv = { .p = db_scale_wm_dac }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 },
1526 {
1527 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1528 .name = "LFE Playback Switch",
1529 .info = wm_mute_info,
1530 .get = wm_mute_get,
1531 .put = wm_mute_put,
1532 .private_value = (1 << 8) | 5
1533 },
1534 {
1535 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001536 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001537 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 .name = "LFE Playback Volume",
1539 .info = wm_vol_info,
1540 .get = wm_vol_get,
1541 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +02001542 .private_value = (1 << 8) | 5,
1543 .tlv = { .p = db_scale_wm_dac }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 },
1545 {
1546 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1547 .name = "Side Playback Switch",
1548 .info = wm_mute_info,
1549 .get = wm_mute_get,
1550 .put = wm_mute_put,
1551 .private_value = (2 << 8) | 6
1552 },
1553 {
1554 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001555 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001556 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 .name = "Side Playback Volume",
1558 .info = wm_vol_info,
1559 .get = wm_vol_get,
1560 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +02001561 .private_value = (2 << 8) | 6,
1562 .tlv = { .p = db_scale_wm_dac }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 }
1564};
1565
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001566static struct snd_kcontrol_new wm_controls[] __devinitdata = {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001567 {
1568 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 .name = "PCM Playback Switch",
1570 .info = wm_pcm_mute_info,
1571 .get = wm_pcm_mute_get,
1572 .put = wm_pcm_mute_put
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001573 },
1574 {
1575 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001576 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001577 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 .name = "PCM Playback Volume",
1579 .info = wm_pcm_vol_info,
1580 .get = wm_pcm_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +02001581 .put = wm_pcm_vol_put,
1582 .tlv = { .p = db_scale_wm_pcm }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001583 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 {
1585 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1586 .name = "Capture Switch",
1587 .info = wm_adc_mute_info,
1588 .get = wm_adc_mute_get,
1589 .put = wm_adc_mute_put,
1590 },
1591 {
1592 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001593 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001594 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 .name = "Capture Volume",
1596 .info = wm_adc_vol_info,
1597 .get = wm_adc_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +02001598 .put = wm_adc_vol_put,
1599 .tlv = { .p = db_scale_wm_adc }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 },
1601 {
1602 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1603 .name = "Capture Source",
1604 .info = wm_adc_mux_info,
1605 .get = wm_adc_mux_get,
1606 .put = wm_adc_mux_put,
1607 .private_value = 5
1608 },
1609 {
1610 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1611 .name = "External Amplifier",
1612 .info = aureon_hpamp_info,
1613 .get = aureon_hpamp_get,
1614 .put = aureon_hpamp_put
1615 },
1616 {
1617 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1618 .name = "DAC Deemphasis Switch",
1619 .info = aureon_deemp_info,
1620 .get = aureon_deemp_get,
1621 .put = aureon_deemp_put
1622 },
1623 {
1624 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1625 .name = "ADC Oversampling",
1626 .info = aureon_oversampling_info,
1627 .get = aureon_oversampling_get,
1628 .put = aureon_oversampling_put
1629 }
1630};
1631
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001632static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001633 {
1634 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 .name = "AC97 Playback Switch",
1636 .info = aureon_ac97_mmute_info,
1637 .get = aureon_ac97_mmute_get,
1638 .put = aureon_ac97_mmute_put,
1639 .private_value = AC97_MASTER
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001640 },
1641 {
1642 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001643 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001644 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1645 .name = "AC97 Playback Volume",
1646 .info = aureon_ac97_vol_info,
1647 .get = aureon_ac97_vol_get,
1648 .put = aureon_ac97_vol_put,
1649 .private_value = AC97_MASTER|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001650 .tlv = { .p = db_scale_ac97_master }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001651 },
1652 {
1653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1654 .name = "CD Playback Switch",
1655 .info = aureon_ac97_mute_info,
1656 .get = aureon_ac97_mute_get,
1657 .put = aureon_ac97_mute_put,
1658 .private_value = AC97_CD
1659 },
1660 {
1661 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001662 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001663 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1664 .name = "CD Playback Volume",
1665 .info = aureon_ac97_vol_info,
1666 .get = aureon_ac97_vol_get,
1667 .put = aureon_ac97_vol_put,
1668 .private_value = AC97_CD|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001669 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001670 },
1671 {
1672 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1673 .name = "Aux Playback Switch",
1674 .info = aureon_ac97_mute_info,
1675 .get = aureon_ac97_mute_get,
1676 .put = aureon_ac97_mute_put,
1677 .private_value = AC97_AUX,
1678 },
1679 {
1680 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001681 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001682 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1683 .name = "Aux Playback Volume",
1684 .info = aureon_ac97_vol_info,
1685 .get = aureon_ac97_vol_get,
1686 .put = aureon_ac97_vol_put,
1687 .private_value = AC97_AUX|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001688 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001689 },
1690 {
1691 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1692 .name = "Line Playback Switch",
1693 .info = aureon_ac97_mute_info,
1694 .get = aureon_ac97_mute_get,
1695 .put = aureon_ac97_mute_put,
1696 .private_value = AC97_LINE
1697 },
1698 {
1699 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001700 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001701 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1702 .name = "Line Playback Volume",
1703 .info = aureon_ac97_vol_info,
1704 .get = aureon_ac97_vol_get,
1705 .put = aureon_ac97_vol_put,
1706 .private_value = AC97_LINE|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001707 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001708 },
1709 {
1710 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1711 .name = "Mic Playback Switch",
1712 .info = aureon_ac97_mute_info,
1713 .get = aureon_ac97_mute_get,
1714 .put = aureon_ac97_mute_put,
1715 .private_value = AC97_MIC
1716 },
1717 {
1718 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001719 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001720 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1721 .name = "Mic Playback Volume",
1722 .info = aureon_ac97_vol_info,
1723 .get = aureon_ac97_vol_get,
1724 .put = aureon_ac97_vol_put,
1725 .private_value = AC97_MIC,
Takashi Iwaif640c322006-08-30 16:57:37 +02001726 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001727 },
1728 {
1729 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1730 .name = "Mic Boost (+20dB)",
1731 .info = aureon_ac97_micboost_info,
1732 .get = aureon_ac97_micboost_get,
1733 .put = aureon_ac97_micboost_put
1734 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735};
1736
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001737static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001738 {
1739 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 .name = "AC97 Playback Switch",
1741 .info = aureon_ac97_mmute_info,
1742 .get = aureon_ac97_mmute_get,
1743 .put = aureon_ac97_mmute_put,
1744 .private_value = AC97_MASTER
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001745 },
1746 {
1747 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001748 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001749 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1750 .name = "AC97 Playback Volume",
1751 .info = aureon_ac97_vol_info,
1752 .get = aureon_ac97_vol_get,
1753 .put = aureon_ac97_vol_put,
1754 .private_value = AC97_MASTER|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001755 .tlv = { .p = db_scale_ac97_master }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001756 },
1757 {
1758 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1759 .name = "CD Playback Switch",
1760 .info = aureon_ac97_mute_info,
1761 .get = aureon_ac97_mute_get,
1762 .put = aureon_ac97_mute_put,
1763 .private_value = AC97_AUX
1764 },
1765 {
1766 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001767 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001768 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1769 .name = "CD Playback Volume",
1770 .info = aureon_ac97_vol_info,
1771 .get = aureon_ac97_vol_get,
1772 .put = aureon_ac97_vol_put,
1773 .private_value = AC97_AUX|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001774 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001775 },
1776 {
1777 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1778 .name = "Phono Playback Switch",
1779 .info = aureon_ac97_mute_info,
1780 .get = aureon_ac97_mute_get,
1781 .put = aureon_ac97_mute_put,
1782 .private_value = AC97_CD
1783 },
1784 {
1785 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001786 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001787 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1788 .name = "Phono Playback Volume",
1789 .info = aureon_ac97_vol_info,
1790 .get = aureon_ac97_vol_get,
1791 .put = aureon_ac97_vol_put,
1792 .private_value = AC97_CD|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001793 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001794 },
1795 {
1796 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1797 .name = "Line Playback Switch",
1798 .info = aureon_ac97_mute_info,
1799 .get = aureon_ac97_mute_get,
1800 .put = aureon_ac97_mute_put,
1801 .private_value = AC97_LINE
1802 },
1803 {
1804 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001805 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001806 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1807 .name = "Line Playback Volume",
1808 .info = aureon_ac97_vol_info,
1809 .get = aureon_ac97_vol_get,
1810 .put = aureon_ac97_vol_put,
1811 .private_value = AC97_LINE|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001812 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001813 },
1814 {
1815 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1816 .name = "Mic Playback Switch",
1817 .info = aureon_ac97_mute_info,
1818 .get = aureon_ac97_mute_get,
1819 .put = aureon_ac97_mute_put,
1820 .private_value = AC97_MIC
1821 },
1822 {
1823 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001824 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001825 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1826 .name = "Mic Playback Volume",
1827 .info = aureon_ac97_vol_info,
1828 .get = aureon_ac97_vol_get,
1829 .put = aureon_ac97_vol_put,
1830 .private_value = AC97_MIC,
Takashi Iwaif640c322006-08-30 16:57:37 +02001831 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001832 },
1833 {
1834 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1835 .name = "Mic Boost (+20dB)",
1836 .info = aureon_ac97_micboost_info,
1837 .get = aureon_ac97_micboost_get,
1838 .put = aureon_ac97_micboost_put
1839 },
1840 {
1841 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1842 .name = "Aux Playback Switch",
1843 .info = aureon_ac97_mute_info,
1844 .get = aureon_ac97_mute_get,
1845 .put = aureon_ac97_mute_put,
1846 .private_value = AC97_VIDEO,
1847 },
1848 {
1849 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +02001850 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001851 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
1852 .name = "Aux Playback Volume",
1853 .info = aureon_ac97_vol_info,
1854 .get = aureon_ac97_vol_get,
1855 .put = aureon_ac97_vol_put,
1856 .private_value = AC97_VIDEO|AUREON_AC97_STEREO,
Takashi Iwaif640c322006-08-30 16:57:37 +02001857 .tlv = { .p = db_scale_ac97_gain }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001858 },
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +02001859 {
1860 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1861 .name = "Aux Source",
1862 .info = aureon_universe_inmux_info,
1863 .get = aureon_universe_inmux_get,
1864 .put = aureon_universe_inmux_put
1865 }
1866
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867};
1868
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001869static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 {
1871 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001872 .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 .info = aureon_cs8415_mute_info,
1874 .get = aureon_cs8415_mute_get,
1875 .put = aureon_cs8415_mute_put
1876 },
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001877 {
1878 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1879 .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Source",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 .info = aureon_cs8415_mux_info,
1881 .get = aureon_cs8415_mux_get,
1882 .put = aureon_cs8415_mux_put,
1883 },
1884 {
1885 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001886 .name = SNDRV_CTL_NAME_IEC958("Q-subcode ", CAPTURE, DEFAULT),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
1888 .info = aureon_cs8415_qsub_info,
1889 .get = aureon_cs8415_qsub_get,
1890 },
1891 {
1892 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001893 .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 .access = SNDRV_CTL_ELEM_ACCESS_READ,
1895 .info = aureon_cs8415_spdif_info,
1896 .get = aureon_cs8415_mask_get
1897 },
1898 {
1899 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001900 .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
1902 .info = aureon_cs8415_spdif_info,
1903 .get = aureon_cs8415_spdif_get
1904 },
1905 {
1906 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001907 .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate",
1908 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 .info = aureon_cs8415_rate_info,
1910 .get = aureon_cs8415_rate_get
1911 }
1912};
1913
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001914static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915{
1916 unsigned int i, counts;
1917 int err;
1918
1919 counts = ARRAY_SIZE(aureon_dac_controls);
1920 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY)
1921 counts -= 2; /* no side */
1922 for (i = 0; i < counts; i++) {
1923 err = snd_ctl_add(ice->card, snd_ctl_new1(&aureon_dac_controls[i], ice));
1924 if (err < 0)
1925 return err;
1926 }
1927
1928 for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
1929 err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
1930 if (err < 0)
1931 return err;
1932 }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001933
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
1935 for (i = 0; i < ARRAY_SIZE(universe_ac97_controls); i++) {
1936 err = snd_ctl_add(ice->card, snd_ctl_new1(&universe_ac97_controls[i], ice));
1937 if (err < 0)
1938 return err;
1939 }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001940 } else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01001941 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) {
1943 err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice));
1944 if (err < 0)
1945 return err;
1946 }
1947 }
1948
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01001949 if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
1950 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 unsigned char id;
1952 snd_ice1712_save_gpio_status(ice);
1953 id = aureon_cs8415_get(ice, CS8415_ID);
1954 if (id != 0x41)
Takashi Iwai99b359b2005-10-20 18:26:44 +02001955 snd_printk(KERN_INFO "No CS8415 chip. Skipping CS8415 controls.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 else if ((id & 0x0F) != 0x01)
Takashi Iwai99b359b2005-10-20 18:26:44 +02001957 snd_printk(KERN_INFO "Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 else {
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001959 for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001960 struct snd_kcontrol *kctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));
1962 if (err < 0)
1963 return err;
1964 if (i > 1)
1965 kctl->id.device = ice->pcm->device;
1966 }
1967 }
1968 snd_ice1712_restore_gpio_status(ice);
1969 }
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04001970
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 return 0;
1972}
1973
1974
1975/*
1976 * initialize the chip
1977 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01001978static int __devinit aureon_init(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979{
Takashi Iwai32b47da2007-01-29 15:26:36 +01001980 static const unsigned short wm_inits_aureon[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 /* These come first to reduce init pop noise */
1982 0x1b, 0x044, /* ADC Mux (AC'97 source) */
1983 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
1984 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
1985
1986 0x18, 0x000, /* All power-up */
1987
1988 0x16, 0x122, /* I2S, normal polarity, 24bit */
1989 0x17, 0x022, /* 256fs, slave mode */
1990 0x00, 0, /* DAC1 analog mute */
1991 0x01, 0, /* DAC2 analog mute */
1992 0x02, 0, /* DAC3 analog mute */
1993 0x03, 0, /* DAC4 analog mute */
1994 0x04, 0, /* DAC5 analog mute */
1995 0x05, 0, /* DAC6 analog mute */
1996 0x06, 0, /* DAC7 analog mute */
1997 0x07, 0, /* DAC8 analog mute */
1998 0x08, 0x100, /* master analog mute */
1999 0x09, 0xff, /* DAC1 digital full */
2000 0x0a, 0xff, /* DAC2 digital full */
2001 0x0b, 0xff, /* DAC3 digital full */
2002 0x0c, 0xff, /* DAC4 digital full */
2003 0x0d, 0xff, /* DAC5 digital full */
2004 0x0e, 0xff, /* DAC6 digital full */
2005 0x0f, 0xff, /* DAC7 digital full */
2006 0x10, 0xff, /* DAC8 digital full */
2007 0x11, 0x1ff, /* master digital full */
2008 0x12, 0x000, /* phase normal */
2009 0x13, 0x090, /* unmute DAC L/R */
2010 0x14, 0x000, /* all unmute */
2011 0x15, 0x000, /* no deemphasis, no ZFLG */
2012 0x19, 0x000, /* -12dB ADC/L */
2013 0x1a, 0x000, /* -12dB ADC/R */
2014 (unsigned short)-1
2015 };
Takashi Iwai32b47da2007-01-29 15:26:36 +01002016 static const unsigned short wm_inits_prodigy[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017
2018 /* These come first to reduce init pop noise */
2019 0x1b, 0x000, /* ADC Mux */
2020 0x1c, 0x009, /* Out Mux1 */
2021 0x1d, 0x009, /* Out Mux2 */
2022
2023 0x18, 0x000, /* All power-up */
2024
2025 0x16, 0x022, /* I2S, normal polarity, 24bit, high-pass on */
2026 0x17, 0x006, /* 128fs, slave mode */
2027
2028 0x00, 0, /* DAC1 analog mute */
2029 0x01, 0, /* DAC2 analog mute */
2030 0x02, 0, /* DAC3 analog mute */
2031 0x03, 0, /* DAC4 analog mute */
2032 0x04, 0, /* DAC5 analog mute */
2033 0x05, 0, /* DAC6 analog mute */
2034 0x06, 0, /* DAC7 analog mute */
2035 0x07, 0, /* DAC8 analog mute */
2036 0x08, 0x100, /* master analog mute */
2037
2038 0x09, 0x7f, /* DAC1 digital full */
2039 0x0a, 0x7f, /* DAC2 digital full */
2040 0x0b, 0x7f, /* DAC3 digital full */
2041 0x0c, 0x7f, /* DAC4 digital full */
2042 0x0d, 0x7f, /* DAC5 digital full */
2043 0x0e, 0x7f, /* DAC6 digital full */
2044 0x0f, 0x7f, /* DAC7 digital full */
2045 0x10, 0x7f, /* DAC8 digital full */
2046 0x11, 0x1FF, /* master digital full */
2047
2048 0x12, 0x000, /* phase normal */
2049 0x13, 0x090, /* unmute DAC L/R */
2050 0x14, 0x000, /* all unmute */
2051 0x15, 0x000, /* no deemphasis, no ZFLG */
2052
2053 0x19, 0x000, /* -12dB ADC/L */
2054 0x1a, 0x000, /* -12dB ADC/R */
2055 (unsigned short)-1
2056
2057 };
Takashi Iwai32b47da2007-01-29 15:26:36 +01002058 static const unsigned short cs_inits[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 0x0441, /* RUN */
2060 0x0180, /* no mute, OMCK output on RMCK pin */
2061 0x0201, /* S/PDIF source on RXP1 */
2062 0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */
2063 (unsigned short)-1
2064 };
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01002065 struct aureon_spec *spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 unsigned int tmp;
Takashi Iwai32b47da2007-01-29 15:26:36 +01002067 const unsigned short *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 int err, i;
2069
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01002070 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2071 if (!spec)
2072 return -ENOMEM;
2073 ice->spec = spec;
2074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
2076 ice->num_total_dacs = 6;
2077 ice->num_total_adcs = 2;
2078 } else {
2079 /* aureon 7.1 and prodigy 7.1 */
2080 ice->num_total_dacs = 8;
2081 ice->num_total_adcs = 2;
2082 }
2083
2084 /* to remeber the register values of CS8415 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +01002085 ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002086 if (!ice->akm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 return -ENOMEM;
2088 ice->akm_codecs = 1;
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002089
2090 err = aureon_ac97_init(ice);
2091 if (err != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 return err;
2093
2094 snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
2095
2096 /* reset the wm codec as the SPI mode */
2097 snd_ice1712_save_gpio_status(ice);
2098 snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RESET|AUREON_WM_CS|AUREON_CS8415_CS|AUREON_HP_SEL));
2099
2100 tmp = snd_ice1712_gpio_read(ice);
2101 tmp &= ~AUREON_WM_RESET;
2102 snd_ice1712_gpio_write(ice, tmp);
2103 udelay(1);
2104 tmp |= AUREON_WM_CS | AUREON_CS8415_CS;
2105 snd_ice1712_gpio_write(ice, tmp);
2106 udelay(1);
2107 tmp |= AUREON_WM_RESET;
2108 snd_ice1712_gpio_write(ice, tmp);
2109 udelay(1);
2110
2111 /* initialize WM8770 codec */
Takashi Iwai45fe7222006-01-13 13:50:16 +01002112 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71 ||
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01002113 ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002114 ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 p = wm_inits_prodigy;
2116 else
2117 p = wm_inits_aureon;
2118 for (; *p != (unsigned short)-1; p += 2)
2119 wm_put(ice, p[0], p[1]);
2120
2121 /* initialize CS8415A codec */
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01002122 if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
2123 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
Takashi Iwai45fe7222006-01-13 13:50:16 +01002124 for (p = cs_inits; *p != (unsigned short)-1; p++)
2125 aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01002126 spec->cs8415_mux = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127
Takashi Iwai45fe7222006-01-13 13:50:16 +01002128 aureon_set_headphone_amp(ice, 1);
2129 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130
2131 snd_ice1712_restore_gpio_status(ice);
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +02002132
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002133 /* initialize PCA9554 pin directions & set default input */
Maximilian Rehkopfaf9b70a2006-03-31 13:10:35 +02002134 aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
2135 aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002136
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01002137 spec->master[0] = WM_VOL_MUTE;
2138 spec->master[1] = WM_VOL_MUTE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 for (i = 0; i < ice->num_total_dacs; i++) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +01002140 spec->vol[i] = WM_VOL_MUTE;
2141 wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 }
2143
2144 return 0;
2145}
2146
2147
2148/*
2149 * Aureon boards don't provide the EEPROM data except for the vendor IDs.
2150 * hence the driver needs to sets up it properly.
2151 */
2152
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01002153static unsigned char aureon51_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +01002154 [ICE_EEP2_SYSCONF] = 0x0a, /* clock 512, spdif-in/ADC, 3DACs */
2155 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
2156 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
2157 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
2158 [ICE_EEP2_GPIO_DIR] = 0xff,
2159 [ICE_EEP2_GPIO_DIR1] = 0xff,
2160 [ICE_EEP2_GPIO_DIR2] = 0x5f,
2161 [ICE_EEP2_GPIO_MASK] = 0x00,
2162 [ICE_EEP2_GPIO_MASK1] = 0x00,
2163 [ICE_EEP2_GPIO_MASK2] = 0x00,
2164 [ICE_EEP2_GPIO_STATE] = 0x00,
2165 [ICE_EEP2_GPIO_STATE1] = 0x00,
2166 [ICE_EEP2_GPIO_STATE2] = 0x00,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167};
2168
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01002169static unsigned char aureon71_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +01002170 [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
2171 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
2172 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
2173 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
2174 [ICE_EEP2_GPIO_DIR] = 0xff,
2175 [ICE_EEP2_GPIO_DIR1] = 0xff,
2176 [ICE_EEP2_GPIO_DIR2] = 0x5f,
2177 [ICE_EEP2_GPIO_MASK] = 0x00,
2178 [ICE_EEP2_GPIO_MASK1] = 0x00,
2179 [ICE_EEP2_GPIO_MASK2] = 0x00,
2180 [ICE_EEP2_GPIO_STATE] = 0x00,
2181 [ICE_EEP2_GPIO_STATE1] = 0x00,
2182 [ICE_EEP2_GPIO_STATE2] = 0x00,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183};
Takashi Iwai189bc172007-01-29 15:25:40 +01002184#define prodigy71_eeprom aureon71_eeprom
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185
Maximilian Rehkopfc1805dd2008-08-29 14:11:10 +02002186static unsigned char aureon71_universe_eeprom[] __devinitdata = {
2187 [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC,
2188 * 4DACs
2189 */
2190 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
2191 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
2192 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
2193 [ICE_EEP2_GPIO_DIR] = 0xff,
2194 [ICE_EEP2_GPIO_DIR1] = 0xff,
2195 [ICE_EEP2_GPIO_DIR2] = 0x5f,
2196 [ICE_EEP2_GPIO_MASK] = 0x00,
2197 [ICE_EEP2_GPIO_MASK1] = 0x00,
2198 [ICE_EEP2_GPIO_MASK2] = 0x00,
2199 [ICE_EEP2_GPIO_STATE] = 0x00,
2200 [ICE_EEP2_GPIO_STATE1] = 0x00,
2201 [ICE_EEP2_GPIO_STATE2] = 0x00,
2202};
2203
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01002204static unsigned char prodigy71lt_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +01002205 [ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */
2206 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
2207 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
2208 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
2209 [ICE_EEP2_GPIO_DIR] = 0xff,
2210 [ICE_EEP2_GPIO_DIR1] = 0xff,
2211 [ICE_EEP2_GPIO_DIR2] = 0x5f,
2212 [ICE_EEP2_GPIO_MASK] = 0x00,
2213 [ICE_EEP2_GPIO_MASK1] = 0x00,
2214 [ICE_EEP2_GPIO_MASK2] = 0x00,
2215 [ICE_EEP2_GPIO_STATE] = 0x00,
2216 [ICE_EEP2_GPIO_STATE1] = 0x00,
2217 [ICE_EEP2_GPIO_STATE2] = 0x00,
Takashi Iwai45fe7222006-01-13 13:50:16 +01002218};
Takashi Iwai189bc172007-01-29 15:25:40 +01002219#define prodigy71xt_eeprom prodigy71lt_eeprom
Takashi Iwai45fe7222006-01-13 13:50:16 +01002220
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221/* entry point */
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01002222struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 {
2224 .subvendor = VT1724_SUBDEVICE_AUREON51_SKY,
2225 .name = "Terratec Aureon 5.1-Sky",
2226 .model = "aureon51",
2227 .chip_init = aureon_init,
2228 .build_controls = aureon_add_controls,
2229 .eeprom_size = sizeof(aureon51_eeprom),
2230 .eeprom_data = aureon51_eeprom,
2231 .driver = "Aureon51",
2232 },
2233 {
2234 .subvendor = VT1724_SUBDEVICE_AUREON71_SPACE,
2235 .name = "Terratec Aureon 7.1-Space",
2236 .model = "aureon71",
2237 .chip_init = aureon_init,
2238 .build_controls = aureon_add_controls,
2239 .eeprom_size = sizeof(aureon71_eeprom),
2240 .eeprom_data = aureon71_eeprom,
2241 .driver = "Aureon71",
2242 },
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002243 {
2244 .subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
2245 .name = "Terratec Aureon 7.1-Universe",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 .model = "universe",
Alexander Beregalov1ce211a2008-09-07 01:19:00 +04002247 .chip_init = aureon_init,
2248 .build_controls = aureon_add_controls,
Maximilian Rehkopfc1805dd2008-08-29 14:11:10 +02002249 .eeprom_size = sizeof(aureon71_universe_eeprom),
2250 .eeprom_data = aureon71_universe_eeprom,
Takashi Iwai9f37c5b2006-06-29 16:40:21 +02002251 .driver = "Aureon71Univ", /* keep in 15 letters */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 },
2253 {
2254 .subvendor = VT1724_SUBDEVICE_PRODIGY71,
2255 .name = "Audiotrak Prodigy 7.1",
2256 .model = "prodigy71",
2257 .chip_init = aureon_init,
2258 .build_controls = aureon_add_controls,
2259 .eeprom_size = sizeof(prodigy71_eeprom),
2260 .eeprom_data = prodigy71_eeprom,
2261 .driver = "Prodigy71", /* should be identical with Aureon71 */
2262 },
Takashi Iwai45fe7222006-01-13 13:50:16 +01002263 {
2264 .subvendor = VT1724_SUBDEVICE_PRODIGY71LT,
2265 .name = "Audiotrak Prodigy 7.1 LT",
2266 .model = "prodigy71lt",
2267 .chip_init = aureon_init,
2268 .build_controls = aureon_add_controls,
2269 .eeprom_size = sizeof(prodigy71lt_eeprom),
2270 .eeprom_data = prodigy71lt_eeprom,
2271 .driver = "Prodigy71LT",
2272 },
Toshimune Konnocdf88ef2006-12-18 13:12:18 +01002273 {
2274 .subvendor = VT1724_SUBDEVICE_PRODIGY71XT,
2275 .name = "Audiotrak Prodigy 7.1 XT",
2276 .model = "prodigy71xt",
2277 .chip_init = aureon_init,
2278 .build_controls = aureon_add_controls,
2279 .eeprom_size = sizeof(prodigy71xt_eeprom),
2280 .eeprom_data = prodigy71xt_eeprom,
2281 .driver = "Prodigy71LT",
2282 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 { } /* terminator */
2284};