blob: f5acdeef4438aaa81ff3218238de2cca20293d22 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * ALSA driver for ICEnsemble ICE1724 (Envy24)
3 *
4 * Lowlevel functions for Terratec PHASE 22
5 *
6 * Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
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
24/* PHASE 22 overview:
25 * Audio controller: VIA Envy24HT-S (slightly trimmed down version of Envy24HT)
26 * Analog chip: AK4524 (partially via Philip's 74HCT125)
27 * Digital receiver: CS8414-CS (not supported in this release)
28 *
29 * Envy connects to AK4524
30 * - CS directly from GPIO 10
31 * - CCLK via 74HCT125's gate #4 from GPIO 4
32 * - CDTI via 74HCT125's gate #2 from GPIO 5
33 * CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
34 */
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <asm/io.h>
37#include <linux/delay.h>
38#include <linux/interrupt.h>
39#include <linux/init.h>
40#include <linux/slab.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010041#include <linux/mutex.h>
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <sound/core.h>
44
45#include "ice1712.h"
46#include "envy24ht.h"
47#include "phase.h"
Takashi Iwaif640c322006-08-30 16:57:37 +020048#include <sound/tlv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Takashi Iwai7cda8ba2008-01-18 13:36:07 +010050/* AC97 register cache for Phase28 */
51struct phase28_spec {
52 unsigned short master[2];
53 unsigned short vol[8];
Harvey Harrison008f3592008-02-29 11:46:32 +010054};
Takashi Iwai7cda8ba2008-01-18 13:36:07 +010055
Simone Zinanniaed058e2005-04-11 14:08:40 +020056/* WM8770 registers */
57#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
58#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
59#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
60#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */
61#define WM_PHASE_SWAP 0x12 /* DAC phase */
62#define WM_DAC_CTRL1 0x13 /* DAC control bits */
63#define WM_MUTE 0x14 /* mute controls */
64#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
65#define WM_INT_CTRL 0x16 /* interface control */
66#define WM_MASTER 0x17 /* master clock and mode */
67#define WM_POWERDOWN 0x18 /* power-down controls */
68#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
69#define WM_ADC_MUX 0x1b /* input MUX */
70#define WM_OUT_MUX1 0x1c /* output MUX */
71#define WM_OUT_MUX2 0x1e /* output MUX */
72#define WM_RESET 0x1f /* software reset */
73
74
75/*
76 * Logarithmic volume values for WM8770
77 * Computed as 20 * Log10(255 / x)
78 */
Takashi Iwai32b47da2007-01-29 15:26:36 +010079static const unsigned char wm_vol[256] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +020080 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
81 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
82 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
83 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
84 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
85 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,
86 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,
87 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,
88 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,
89 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,
90 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,
91 0, 0
92};
93
94#define WM_VOL_MAX (sizeof(wm_vol) - 1)
95#define WM_VOL_MUTE 0x8000
96
Takashi Iwai1b60f6b2007-03-13 22:13:47 +010097static struct snd_akm4xxx akm_phase22 __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 .type = SND_AK4524,
99 .num_dacs = 2,
100 .num_adcs = 2,
101};
102
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100103static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 .caddr = 2,
105 .cif = 1,
106 .data_mask = 1 << 4,
107 .clk_mask = 1 << 5,
108 .cs_mask = 1 << 10,
109 .cs_addr = 1 << 10,
110 .cs_none = 0,
111 .add_flags = 1 << 3,
112 .mask_flags = 0,
113};
114
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100115static int __devinit phase22_init(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100117 struct snd_akm4xxx *ak;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 int err;
119
120 // Configure DAC/ADC description for generic part of ice1724
121 switch (ice->eeprom.subvendor) {
122 case VT1724_SUBDEVICE_PHASE22:
Misha Zhilin740dc9c2008-08-01 12:45:14 +0200123 case VT1724_SUBDEVICE_TS22:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 ice->num_total_dacs = 2;
125 ice->num_total_adcs = 2;
126 ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
127 break;
128 default:
129 snd_BUG();
130 return -EINVAL;
131 }
132
133 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100134 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 if (! ak)
136 return -ENOMEM;
137 ice->akm_codecs = 1;
138 switch (ice->eeprom.subvendor) {
139 case VT1724_SUBDEVICE_PHASE22:
Misha Zhilin740dc9c2008-08-01 12:45:14 +0200140 case VT1724_SUBDEVICE_TS22:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
142 return err;
143 break;
144 }
145
146 return 0;
147}
148
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100149static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
151 int err = 0;
152
153 switch (ice->eeprom.subvendor) {
154 case VT1724_SUBDEVICE_PHASE22:
Misha Zhilin740dc9c2008-08-01 12:45:14 +0200155 case VT1724_SUBDEVICE_TS22:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 err = snd_ice1712_akm4xxx_build_controls(ice);
157 if (err < 0)
158 return err;
159 }
160 return 0;
161}
162
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100163static unsigned char phase22_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +0100164 [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */
165 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
166 [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */
167 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
168 [ICE_EEP2_GPIO_DIR] = 0xff,
169 [ICE_EEP2_GPIO_DIR1] = 0xff,
170 [ICE_EEP2_GPIO_DIR2] = 0xff,
171 [ICE_EEP2_GPIO_MASK] = 0x00,
172 [ICE_EEP2_GPIO_MASK1] = 0x00,
173 [ICE_EEP2_GPIO_MASK2] = 0x00,
174 [ICE_EEP2_GPIO_STATE] = 0x00,
175 [ICE_EEP2_GPIO_STATE1] = 0x00,
176 [ICE_EEP2_GPIO_STATE2] = 0x00,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177};
178
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100179static unsigned char phase28_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +0100180 [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
181 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
182 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
183 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
184 [ICE_EEP2_GPIO_DIR] = 0xff,
185 [ICE_EEP2_GPIO_DIR1] = 0xff,
186 [ICE_EEP2_GPIO_DIR2] = 0x5f,
187 [ICE_EEP2_GPIO_MASK] = 0x00,
188 [ICE_EEP2_GPIO_MASK1] = 0x00,
189 [ICE_EEP2_GPIO_MASK2] = 0x00,
190 [ICE_EEP2_GPIO_STATE] = 0x00,
191 [ICE_EEP2_GPIO_STATE1] = 0x00,
192 [ICE_EEP2_GPIO_STATE2] = 0x00,
Simone Zinanniaed058e2005-04-11 14:08:40 +0200193};
194
195/*
196 * write data in the SPI mode
197 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100198static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200199{
200 unsigned int tmp;
201 int i;
202
203 tmp = snd_ice1712_gpio_read(ice);
204
205 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
206 PHASE28_WM_CS));
207 tmp |= PHASE28_WM_RW;
208 tmp &= ~cs;
209 snd_ice1712_gpio_write(ice, tmp);
210 udelay(1);
211
212 for (i = bits - 1; i >= 0; i--) {
213 tmp &= ~PHASE28_SPI_CLK;
214 snd_ice1712_gpio_write(ice, tmp);
215 udelay(1);
216 if (data & (1 << i))
217 tmp |= PHASE28_SPI_MOSI;
218 else
219 tmp &= ~PHASE28_SPI_MOSI;
220 snd_ice1712_gpio_write(ice, tmp);
221 udelay(1);
222 tmp |= PHASE28_SPI_CLK;
223 snd_ice1712_gpio_write(ice, tmp);
224 udelay(1);
225 }
226
227 tmp &= ~PHASE28_SPI_CLK;
228 tmp |= cs;
229 snd_ice1712_gpio_write(ice, tmp);
230 udelay(1);
231 tmp |= PHASE28_SPI_CLK;
232 snd_ice1712_gpio_write(ice, tmp);
233 udelay(1);
234}
235
236/*
237 * get the current register value of WM codec
238 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100239static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200240{
241 reg <<= 1;
242 return ((unsigned short)ice->akm[0].images[reg] << 8) |
243 ice->akm[0].images[reg + 1];
244}
245
246/*
247 * set the register value of WM codec
248 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100249static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200250{
251 phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
252}
253
254/*
255 * set the register value of WM codec and remember it
256 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100257static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200258{
259 wm_put_nocache(ice, reg, val);
260 reg <<= 1;
261 ice->akm[0].images[reg] = val >> 8;
262 ice->akm[0].images[reg + 1] = val;
263}
264
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100265static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200266{
267 unsigned char nvol;
268
269 if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
270 nvol = 0;
271 else
272 nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
273
274 wm_put(ice, index, nvol);
275 wm_put_nocache(ice, index, 0x180 | nvol);
276}
277
278/*
279 * DAC mute control
280 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200281#define wm_pcm_mute_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200282
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100283static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200284{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100285 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200286
Ingo Molnar62932df2006-01-16 16:34:20 +0100287 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200288 ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
Ingo Molnar62932df2006-01-16 16:34:20 +0100289 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200290 return 0;
291}
292
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100293static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200294{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100295 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200296 unsigned short nval, oval;
297 int change;
298
299 snd_ice1712_save_gpio_status(ice);
300 oval = wm_get(ice, WM_MUTE);
301 nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
302 if ((change = (nval != oval)))
303 wm_put(ice, WM_MUTE, nval);
304 snd_ice1712_restore_gpio_status(ice);
305
306 return change;
307}
308
309/*
310 * Master volume attenuation mixer control
311 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100312static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200313{
314 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
315 uinfo->count = 2;
316 uinfo->value.integer.min = 0;
317 uinfo->value.integer.max = WM_VOL_MAX;
318 return 0;
319}
320
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100321static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200322{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100323 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100324 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200325 int i;
326 for (i=0; i<2; i++)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100327 ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200328 return 0;
329}
330
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100331static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200332{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100333 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100334 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200335 int ch, change = 0;
336
337 snd_ice1712_save_gpio_status(ice);
338 for (ch = 0; ch < 2; ch++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100339 unsigned int vol = ucontrol->value.integer.value[ch];
340 if (vol > WM_VOL_MAX)
341 continue;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100342 vol |= spec->master[ch] & WM_VOL_MUTE;
343 if (vol != spec->master[ch]) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200344 int dac;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100345 spec->master[ch] = vol;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200346 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
347 wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100348 spec->vol[dac + ch],
349 spec->master[ch]);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200350 change = 1;
351 }
352 }
353 snd_ice1712_restore_gpio_status(ice);
354 return change;
355}
356
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100357static int __devinit phase28_init(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200358{
Takashi Iwai32b47da2007-01-29 15:26:36 +0100359 static const unsigned short wm_inits_phase28[] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200360 /* These come first to reduce init pop noise */
361 0x1b, 0x044, /* ADC Mux (AC'97 source) */
362 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
363 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
364
365 0x18, 0x000, /* All power-up */
366
367 0x16, 0x122, /* I2S, normal polarity, 24bit */
368 0x17, 0x022, /* 256fs, slave mode */
369 0x00, 0, /* DAC1 analog mute */
370 0x01, 0, /* DAC2 analog mute */
371 0x02, 0, /* DAC3 analog mute */
372 0x03, 0, /* DAC4 analog mute */
373 0x04, 0, /* DAC5 analog mute */
374 0x05, 0, /* DAC6 analog mute */
375 0x06, 0, /* DAC7 analog mute */
376 0x07, 0, /* DAC8 analog mute */
377 0x08, 0x100, /* master analog mute */
378 0x09, 0xff, /* DAC1 digital full */
379 0x0a, 0xff, /* DAC2 digital full */
380 0x0b, 0xff, /* DAC3 digital full */
381 0x0c, 0xff, /* DAC4 digital full */
382 0x0d, 0xff, /* DAC5 digital full */
383 0x0e, 0xff, /* DAC6 digital full */
384 0x0f, 0xff, /* DAC7 digital full */
385 0x10, 0xff, /* DAC8 digital full */
386 0x11, 0x1ff, /* master digital full */
387 0x12, 0x000, /* phase normal */
388 0x13, 0x090, /* unmute DAC L/R */
389 0x14, 0x000, /* all unmute */
390 0x15, 0x000, /* no deemphasis, no ZFLG */
391 0x19, 0x000, /* -12dB ADC/L */
392 0x1a, 0x000, /* -12dB ADC/R */
393 (unsigned short)-1
394 };
395
396 unsigned int tmp;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100397 struct snd_akm4xxx *ak;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100398 struct phase28_spec *spec;
Takashi Iwai32b47da2007-01-29 15:26:36 +0100399 const unsigned short *p;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200400 int i;
401
402 ice->num_total_dacs = 8;
403 ice->num_total_adcs = 2;
404
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100405 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
406 if (!spec)
407 return -ENOMEM;
408 ice->spec = spec;
409
Simone Zinanniaed058e2005-04-11 14:08:40 +0200410 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100411 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200412 if (!ak)
413 return -ENOMEM;
414 ice->akm_codecs = 1;
415
416 snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
417
418 /* reset the wm codec as the SPI mode */
419 snd_ice1712_save_gpio_status(ice);
420 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
421
422 tmp = snd_ice1712_gpio_read(ice);
423 tmp &= ~PHASE28_WM_RESET;
424 snd_ice1712_gpio_write(ice, tmp);
425 udelay(1);
426 tmp |= PHASE28_WM_CS;
427 snd_ice1712_gpio_write(ice, tmp);
428 udelay(1);
429 tmp |= PHASE28_WM_RESET;
430 snd_ice1712_gpio_write(ice, tmp);
431 udelay(1);
432
433 p = wm_inits_phase28;
434 for (; *p != (unsigned short)-1; p += 2)
435 wm_put(ice, p[0], p[1]);
436
437 snd_ice1712_restore_gpio_status(ice);
438
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100439 spec->master[0] = WM_VOL_MUTE;
440 spec->master[1] = WM_VOL_MUTE;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200441 for (i = 0; i < ice->num_total_dacs; i++) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100442 spec->vol[i] = WM_VOL_MUTE;
443 wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200444 }
445
446 return 0;
447}
448
449/*
450 * DAC volume attenuation mixer control
451 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100452static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200453{
454 int voices = kcontrol->private_value >> 8;
455 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
456 uinfo->count = voices;
457 uinfo->value.integer.min = 0; /* mute (-101dB) */
458 uinfo->value.integer.max = 0x7F; /* 0dB */
459 return 0;
460}
461
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100462static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200463{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100464 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100465 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200466 int i, ofs, voices;
467
468 voices = kcontrol->private_value >> 8;
469 ofs = kcontrol->private_value & 0xff;
470 for (i = 0; i < voices; i++)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100471 ucontrol->value.integer.value[i] =
472 spec->vol[ofs+i] & ~WM_VOL_MUTE;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200473 return 0;
474}
475
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100476static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200477{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100478 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100479 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200480 int i, idx, ofs, voices;
481 int change = 0;
482
483 voices = kcontrol->private_value >> 8;
484 ofs = kcontrol->private_value & 0xff;
485 snd_ice1712_save_gpio_status(ice);
486 for (i = 0; i < voices; i++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100487 unsigned int vol;
488 vol = ucontrol->value.integer.value[i];
489 if (vol > 0x7f)
490 continue;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100491 vol |= spec->vol[ofs+i] & WM_VOL_MUTE;
492 if (vol != spec->vol[ofs+i]) {
493 spec->vol[ofs+i] = vol;
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100494 idx = WM_DAC_ATTEN + ofs + i;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100495 wm_set_vol(ice, idx, spec->vol[ofs+i],
496 spec->master[i]);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200497 change = 1;
498 }
499 }
500 snd_ice1712_restore_gpio_status(ice);
501 return change;
502}
503
504/*
505 * WM8770 mute control
506 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100507static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200508 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
509 uinfo->count = kcontrol->private_value >> 8;
510 uinfo->value.integer.min = 0;
511 uinfo->value.integer.max = 1;
512 return 0;
513}
514
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100515static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200516{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100517 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100518 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200519 int voices, ofs, i;
520
521 voices = kcontrol->private_value >> 8;
522 ofs = kcontrol->private_value & 0xFF;
523
524 for (i = 0; i < voices; i++)
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100525 ucontrol->value.integer.value[i] =
526 (spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200527 return 0;
528}
529
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100530static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200531{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100532 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100533 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200534 int change = 0, voices, ofs, i;
535
536 voices = kcontrol->private_value >> 8;
537 ofs = kcontrol->private_value & 0xFF;
538
539 snd_ice1712_save_gpio_status(ice);
540 for (i = 0; i < voices; i++) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100541 int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200542 if (ucontrol->value.integer.value[i] != val) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100543 spec->vol[ofs + i] &= ~WM_VOL_MUTE;
544 spec->vol[ofs + i] |=
Simone Zinanniaed058e2005-04-11 14:08:40 +0200545 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100546 wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
547 spec->master[i]);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200548 change = 1;
549 }
550 }
551 snd_ice1712_restore_gpio_status(ice);
552
553 return change;
554}
555
556/*
557 * WM8770 master mute control
558 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200559#define wm_master_mute_info snd_ctl_boolean_stereo_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200560
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100561static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200562{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100563 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100564 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200565
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100566 ucontrol->value.integer.value[0] =
567 (spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
568 ucontrol->value.integer.value[1] =
569 (spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200570 return 0;
571}
572
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100573static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200574{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100575 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100576 struct phase28_spec *spec = ice->spec;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200577 int change = 0, i;
578
579 snd_ice1712_save_gpio_status(ice);
580 for (i = 0; i < 2; i++) {
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100581 int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200582 if (ucontrol->value.integer.value[i] != val) {
583 int dac;
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100584 spec->master[i] &= ~WM_VOL_MUTE;
585 spec->master[i] |=
Simone Zinanniaed058e2005-04-11 14:08:40 +0200586 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
587 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
588 wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
Takashi Iwai7cda8ba2008-01-18 13:36:07 +0100589 spec->vol[dac + i],
590 spec->master[i]);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200591 change = 1;
592 }
593 }
594 snd_ice1712_restore_gpio_status(ice);
595
596 return change;
597}
598
599/* digital master volume */
600#define PCM_0dB 0xff
601#define PCM_RES 128 /* -64dB */
602#define PCM_MIN (PCM_0dB - PCM_RES)
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100603static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200604{
605 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
606 uinfo->count = 1;
607 uinfo->value.integer.min = 0; /* mute (-64dB) */
608 uinfo->value.integer.max = PCM_RES; /* 0dB */
609 return 0;
610}
611
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100612static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200613{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100614 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200615 unsigned short val;
616
Ingo Molnar62932df2006-01-16 16:34:20 +0100617 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200618 val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
619 val = val > PCM_MIN ? (val - PCM_MIN) : 0;
620 ucontrol->value.integer.value[0] = val;
Ingo Molnar62932df2006-01-16 16:34:20 +0100621 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200622 return 0;
623}
624
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100625static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200626{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100627 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200628 unsigned short ovol, nvol;
629 int change = 0;
630
Simone Zinanniaed058e2005-04-11 14:08:40 +0200631 nvol = ucontrol->value.integer.value[0];
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100632 if (nvol > PCM_RES)
633 return -EINVAL;
634 snd_ice1712_save_gpio_status(ice);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200635 nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
636 ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
637 if (ovol != nvol) {
638 wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
639 wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
640 change = 1;
641 }
642 snd_ice1712_restore_gpio_status(ice);
643 return change;
644}
645
646/*
Simone Zinanniaed058e2005-04-11 14:08:40 +0200647 * Deemphasis
648 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200649#define phase28_deemp_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200650
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100651static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200652{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100653 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200654 ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
655 return 0;
656}
657
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100658static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200659{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100660 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200661 int temp, temp2;
662 temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
663 if (ucontrol->value.integer.value[0])
664 temp |= 0xf;
665 else
666 temp &= ~0xf;
667 if (temp != temp2) {
668 wm_put(ice, WM_DAC_CTRL2, temp);
669 return 1;
670 }
671 return 0;
672}
673
674/*
675 * ADC Oversampling
676 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100677static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200678{
679 static char *texts[2] = { "128x", "64x" };
680
681 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
682 uinfo->count = 1;
683 uinfo->value.enumerated.items = 2;
684
685 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
686 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
687 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
688
689 return 0;
690}
691
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100692static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200693{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100694 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200695 ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
696 return 0;
697}
698
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100699static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200700{
701 int temp, temp2;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100702 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200703
704 temp2 = temp = wm_get(ice, WM_MASTER);
705
706 if (ucontrol->value.enumerated.item[0])
707 temp |= 0x8;
708 else
709 temp &= ~0x8;
710
711 if (temp != temp2) {
712 wm_put(ice, WM_MASTER, temp);
713 return 1;
714 }
715 return 0;
716}
717
Takashi Iwai0cb29ea2007-01-29 15:33:49 +0100718static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
719static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
Takashi Iwaif640c322006-08-30 16:57:37 +0200720
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100721static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200722 {
723 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
724 .name = "Master Playback Switch",
725 .info = wm_master_mute_info,
726 .get = wm_master_mute_get,
727 .put = wm_master_mute_put
728 },
729 {
730 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200731 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
732 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200733 .name = "Master Playback Volume",
734 .info = wm_master_vol_info,
735 .get = wm_master_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200736 .put = wm_master_vol_put,
737 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200738 },
739 {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741 .name = "Front Playback Switch",
742 .info = wm_mute_info,
743 .get = wm_mute_get,
744 .put = wm_mute_put,
745 .private_value = (2 << 8) | 0
746 },
747 {
748 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200749 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
750 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200751 .name = "Front Playback Volume",
752 .info = wm_vol_info,
753 .get = wm_vol_get,
754 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200755 .private_value = (2 << 8) | 0,
756 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200757 },
758 {
759 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
760 .name = "Rear Playback Switch",
761 .info = wm_mute_info,
762 .get = wm_mute_get,
763 .put = wm_mute_put,
764 .private_value = (2 << 8) | 2
765 },
766 {
767 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200768 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
769 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200770 .name = "Rear Playback Volume",
771 .info = wm_vol_info,
772 .get = wm_vol_get,
773 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200774 .private_value = (2 << 8) | 2,
775 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200776 },
777 {
778 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
779 .name = "Center Playback Switch",
780 .info = wm_mute_info,
781 .get = wm_mute_get,
782 .put = wm_mute_put,
783 .private_value = (1 << 8) | 4
784 },
785 {
786 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200787 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
788 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200789 .name = "Center Playback Volume",
790 .info = wm_vol_info,
791 .get = wm_vol_get,
792 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200793 .private_value = (1 << 8) | 4,
794 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200795 },
796 {
797 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
798 .name = "LFE Playback Switch",
799 .info = wm_mute_info,
800 .get = wm_mute_get,
801 .put = wm_mute_put,
802 .private_value = (1 << 8) | 5
803 },
804 {
805 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200806 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
807 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200808 .name = "LFE Playback Volume",
809 .info = wm_vol_info,
810 .get = wm_vol_get,
811 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200812 .private_value = (1 << 8) | 5,
813 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200814 },
815 {
816 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
817 .name = "Side Playback Switch",
818 .info = wm_mute_info,
819 .get = wm_mute_get,
820 .put = wm_mute_put,
821 .private_value = (2 << 8) | 6
822 },
823 {
824 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200825 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
826 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200827 .name = "Side Playback Volume",
828 .info = wm_vol_info,
829 .get = wm_vol_get,
830 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200831 .private_value = (2 << 8) | 6,
832 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200833 }
834};
835
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100836static struct snd_kcontrol_new wm_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200837 {
838 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
839 .name = "PCM Playback Switch",
840 .info = wm_pcm_mute_info,
841 .get = wm_pcm_mute_get,
842 .put = wm_pcm_mute_put
843 },
844 {
845 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200846 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
847 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200848 .name = "PCM Playback Volume",
849 .info = wm_pcm_vol_info,
850 .get = wm_pcm_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200851 .put = wm_pcm_vol_put,
852 .tlv = { .p = db_scale_wm_pcm }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200853 },
854 {
855 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
856 .name = "DAC Deemphasis Switch",
857 .info = phase28_deemp_info,
858 .get = phase28_deemp_get,
859 .put = phase28_deemp_put
860 },
861 {
862 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
863 .name = "ADC Oversampling",
864 .info = phase28_oversampling_info,
865 .get = phase28_oversampling_get,
866 .put = phase28_oversampling_put
867 }
868};
869
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100870static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200871{
872 unsigned int i, counts;
873 int err;
874
875 counts = ARRAY_SIZE(phase28_dac_controls);
876 for (i = 0; i < counts; i++) {
877 err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
878 if (err < 0)
879 return err;
880 }
881
882 for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
883 err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
884 if (err < 0)
885 return err;
886 }
887
888 return 0;
889}
890
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100891struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 {
893 .subvendor = VT1724_SUBDEVICE_PHASE22,
894 .name = "Terratec PHASE 22",
895 .model = "phase22",
896 .chip_init = phase22_init,
897 .build_controls = phase22_add_controls,
898 .eeprom_size = sizeof(phase22_eeprom),
899 .eeprom_data = phase22_eeprom,
900 },
Simone Zinanniaed058e2005-04-11 14:08:40 +0200901 {
902 .subvendor = VT1724_SUBDEVICE_PHASE28,
903 .name = "Terratec PHASE 28",
904 .model = "phase28",
905 .chip_init = phase28_init,
906 .build_controls = phase28_add_controls,
907 .eeprom_size = sizeof(phase28_eeprom),
908 .eeprom_data = phase28_eeprom,
909 },
Misha Zhilin740dc9c2008-08-01 12:45:14 +0200910 {
911 .subvendor = VT1724_SUBDEVICE_TS22,
912 .name = "Terrasoniq TS22 PCI",
913 .model = "TS22",
914 .chip_init = phase22_init,
915 .build_controls = phase22_add_controls,
916 .eeprom_size = sizeof(phase22_eeprom),
917 .eeprom_data = phase22_eeprom,
918 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 { } /* terminator */
920};