blob: 718e9359e1f2a98f82cfd162dd6e8bbb9f68398e [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
Simone Zinanniaed058e2005-04-11 14:08:40 +020050/* WM8770 registers */
51#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
52#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
53#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
54#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */
55#define WM_PHASE_SWAP 0x12 /* DAC phase */
56#define WM_DAC_CTRL1 0x13 /* DAC control bits */
57#define WM_MUTE 0x14 /* mute controls */
58#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
59#define WM_INT_CTRL 0x16 /* interface control */
60#define WM_MASTER 0x17 /* master clock and mode */
61#define WM_POWERDOWN 0x18 /* power-down controls */
62#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
63#define WM_ADC_MUX 0x1b /* input MUX */
64#define WM_OUT_MUX1 0x1c /* output MUX */
65#define WM_OUT_MUX2 0x1e /* output MUX */
66#define WM_RESET 0x1f /* software reset */
67
68
69/*
70 * Logarithmic volume values for WM8770
71 * Computed as 20 * Log10(255 / x)
72 */
Takashi Iwai32b47da2007-01-29 15:26:36 +010073static const unsigned char wm_vol[256] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +020074 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
75 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
76 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
77 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
78 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
79 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,
80 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,
81 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,
82 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,
83 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,
84 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,
85 0, 0
86};
87
88#define WM_VOL_MAX (sizeof(wm_vol) - 1)
89#define WM_VOL_MUTE 0x8000
90
Takashi Iwai1b60f6b2007-03-13 22:13:47 +010091static struct snd_akm4xxx akm_phase22 __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 .type = SND_AK4524,
93 .num_dacs = 2,
94 .num_adcs = 2,
95};
96
Takashi Iwai1b60f6b2007-03-13 22:13:47 +010097static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 .caddr = 2,
99 .cif = 1,
100 .data_mask = 1 << 4,
101 .clk_mask = 1 << 5,
102 .cs_mask = 1 << 10,
103 .cs_addr = 1 << 10,
104 .cs_none = 0,
105 .add_flags = 1 << 3,
106 .mask_flags = 0,
107};
108
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100109static int __devinit phase22_init(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100111 struct snd_akm4xxx *ak;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 int err;
113
114 // Configure DAC/ADC description for generic part of ice1724
115 switch (ice->eeprom.subvendor) {
116 case VT1724_SUBDEVICE_PHASE22:
117 ice->num_total_dacs = 2;
118 ice->num_total_adcs = 2;
119 ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
120 break;
121 default:
122 snd_BUG();
123 return -EINVAL;
124 }
125
126 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100127 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 if (! ak)
129 return -ENOMEM;
130 ice->akm_codecs = 1;
131 switch (ice->eeprom.subvendor) {
132 case VT1724_SUBDEVICE_PHASE22:
133 if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
134 return err;
135 break;
136 }
137
138 return 0;
139}
140
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100141static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
143 int err = 0;
144
145 switch (ice->eeprom.subvendor) {
146 case VT1724_SUBDEVICE_PHASE22:
147 err = snd_ice1712_akm4xxx_build_controls(ice);
148 if (err < 0)
149 return err;
150 }
151 return 0;
152}
153
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100154static unsigned char phase22_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +0100155 [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */
156 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
157 [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */
158 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
159 [ICE_EEP2_GPIO_DIR] = 0xff,
160 [ICE_EEP2_GPIO_DIR1] = 0xff,
161 [ICE_EEP2_GPIO_DIR2] = 0xff,
162 [ICE_EEP2_GPIO_MASK] = 0x00,
163 [ICE_EEP2_GPIO_MASK1] = 0x00,
164 [ICE_EEP2_GPIO_MASK2] = 0x00,
165 [ICE_EEP2_GPIO_STATE] = 0x00,
166 [ICE_EEP2_GPIO_STATE1] = 0x00,
167 [ICE_EEP2_GPIO_STATE2] = 0x00,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168};
169
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100170static unsigned char phase28_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +0100171 [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
172 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
173 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
174 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
175 [ICE_EEP2_GPIO_DIR] = 0xff,
176 [ICE_EEP2_GPIO_DIR1] = 0xff,
177 [ICE_EEP2_GPIO_DIR2] = 0x5f,
178 [ICE_EEP2_GPIO_MASK] = 0x00,
179 [ICE_EEP2_GPIO_MASK1] = 0x00,
180 [ICE_EEP2_GPIO_MASK2] = 0x00,
181 [ICE_EEP2_GPIO_STATE] = 0x00,
182 [ICE_EEP2_GPIO_STATE1] = 0x00,
183 [ICE_EEP2_GPIO_STATE2] = 0x00,
Simone Zinanniaed058e2005-04-11 14:08:40 +0200184};
185
186/*
187 * write data in the SPI mode
188 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100189static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200190{
191 unsigned int tmp;
192 int i;
193
194 tmp = snd_ice1712_gpio_read(ice);
195
196 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
197 PHASE28_WM_CS));
198 tmp |= PHASE28_WM_RW;
199 tmp &= ~cs;
200 snd_ice1712_gpio_write(ice, tmp);
201 udelay(1);
202
203 for (i = bits - 1; i >= 0; i--) {
204 tmp &= ~PHASE28_SPI_CLK;
205 snd_ice1712_gpio_write(ice, tmp);
206 udelay(1);
207 if (data & (1 << i))
208 tmp |= PHASE28_SPI_MOSI;
209 else
210 tmp &= ~PHASE28_SPI_MOSI;
211 snd_ice1712_gpio_write(ice, tmp);
212 udelay(1);
213 tmp |= PHASE28_SPI_CLK;
214 snd_ice1712_gpio_write(ice, tmp);
215 udelay(1);
216 }
217
218 tmp &= ~PHASE28_SPI_CLK;
219 tmp |= cs;
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/*
228 * get the current register value of WM codec
229 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100230static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200231{
232 reg <<= 1;
233 return ((unsigned short)ice->akm[0].images[reg] << 8) |
234 ice->akm[0].images[reg + 1];
235}
236
237/*
238 * set the register value of WM codec
239 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100240static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200241{
242 phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
243}
244
245/*
246 * set the register value of WM codec and remember it
247 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100248static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200249{
250 wm_put_nocache(ice, reg, val);
251 reg <<= 1;
252 ice->akm[0].images[reg] = val >> 8;
253 ice->akm[0].images[reg + 1] = val;
254}
255
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100256static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200257{
258 unsigned char nvol;
259
260 if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
261 nvol = 0;
262 else
263 nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
264
265 wm_put(ice, index, nvol);
266 wm_put_nocache(ice, index, 0x180 | nvol);
267}
268
269/*
270 * DAC mute control
271 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200272#define wm_pcm_mute_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200273
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100274static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200275{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100276 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200277
Ingo Molnar62932df2006-01-16 16:34:20 +0100278 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200279 ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
Ingo Molnar62932df2006-01-16 16:34:20 +0100280 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200281 return 0;
282}
283
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100284static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200285{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100286 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200287 unsigned short nval, oval;
288 int change;
289
290 snd_ice1712_save_gpio_status(ice);
291 oval = wm_get(ice, WM_MUTE);
292 nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
293 if ((change = (nval != oval)))
294 wm_put(ice, WM_MUTE, nval);
295 snd_ice1712_restore_gpio_status(ice);
296
297 return change;
298}
299
300/*
301 * Master volume attenuation mixer control
302 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100303static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200304{
305 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
306 uinfo->count = 2;
307 uinfo->value.integer.min = 0;
308 uinfo->value.integer.max = WM_VOL_MAX;
309 return 0;
310}
311
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100312static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200313{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100314 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200315 int i;
316 for (i=0; i<2; i++)
317 ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
318 return 0;
319}
320
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100321static int wm_master_vol_put(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);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200324 int ch, change = 0;
325
326 snd_ice1712_save_gpio_status(ice);
327 for (ch = 0; ch < 2; ch++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100328 unsigned int vol = ucontrol->value.integer.value[ch];
329 if (vol > WM_VOL_MAX)
330 continue;
331 vol |= ice->spec.phase28.master[ch] & WM_VOL_MUTE;
332 if (vol != ice->spec.phase28.master[ch]) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200333 int dac;
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100334 ice->spec.phase28.master[ch] = vol;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200335 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
336 wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
337 ice->spec.phase28.vol[dac + ch],
338 ice->spec.phase28.master[ch]);
339 change = 1;
340 }
341 }
342 snd_ice1712_restore_gpio_status(ice);
343 return change;
344}
345
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100346static int __devinit phase28_init(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200347{
Takashi Iwai32b47da2007-01-29 15:26:36 +0100348 static const unsigned short wm_inits_phase28[] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200349 /* These come first to reduce init pop noise */
350 0x1b, 0x044, /* ADC Mux (AC'97 source) */
351 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
352 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
353
354 0x18, 0x000, /* All power-up */
355
356 0x16, 0x122, /* I2S, normal polarity, 24bit */
357 0x17, 0x022, /* 256fs, slave mode */
358 0x00, 0, /* DAC1 analog mute */
359 0x01, 0, /* DAC2 analog mute */
360 0x02, 0, /* DAC3 analog mute */
361 0x03, 0, /* DAC4 analog mute */
362 0x04, 0, /* DAC5 analog mute */
363 0x05, 0, /* DAC6 analog mute */
364 0x06, 0, /* DAC7 analog mute */
365 0x07, 0, /* DAC8 analog mute */
366 0x08, 0x100, /* master analog mute */
367 0x09, 0xff, /* DAC1 digital full */
368 0x0a, 0xff, /* DAC2 digital full */
369 0x0b, 0xff, /* DAC3 digital full */
370 0x0c, 0xff, /* DAC4 digital full */
371 0x0d, 0xff, /* DAC5 digital full */
372 0x0e, 0xff, /* DAC6 digital full */
373 0x0f, 0xff, /* DAC7 digital full */
374 0x10, 0xff, /* DAC8 digital full */
375 0x11, 0x1ff, /* master digital full */
376 0x12, 0x000, /* phase normal */
377 0x13, 0x090, /* unmute DAC L/R */
378 0x14, 0x000, /* all unmute */
379 0x15, 0x000, /* no deemphasis, no ZFLG */
380 0x19, 0x000, /* -12dB ADC/L */
381 0x1a, 0x000, /* -12dB ADC/R */
382 (unsigned short)-1
383 };
384
385 unsigned int tmp;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100386 struct snd_akm4xxx *ak;
Takashi Iwai32b47da2007-01-29 15:26:36 +0100387 const unsigned short *p;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200388 int i;
389
390 ice->num_total_dacs = 8;
391 ice->num_total_adcs = 2;
392
393 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100394 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200395 if (!ak)
396 return -ENOMEM;
397 ice->akm_codecs = 1;
398
399 snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
400
401 /* reset the wm codec as the SPI mode */
402 snd_ice1712_save_gpio_status(ice);
403 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
404
405 tmp = snd_ice1712_gpio_read(ice);
406 tmp &= ~PHASE28_WM_RESET;
407 snd_ice1712_gpio_write(ice, tmp);
408 udelay(1);
409 tmp |= PHASE28_WM_CS;
410 snd_ice1712_gpio_write(ice, tmp);
411 udelay(1);
412 tmp |= PHASE28_WM_RESET;
413 snd_ice1712_gpio_write(ice, tmp);
414 udelay(1);
415
416 p = wm_inits_phase28;
417 for (; *p != (unsigned short)-1; p += 2)
418 wm_put(ice, p[0], p[1]);
419
420 snd_ice1712_restore_gpio_status(ice);
421
422 ice->spec.phase28.master[0] = WM_VOL_MUTE;
423 ice->spec.phase28.master[1] = WM_VOL_MUTE;
424 for (i = 0; i < ice->num_total_dacs; i++) {
425 ice->spec.phase28.vol[i] = WM_VOL_MUTE;
426 wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
427 }
428
429 return 0;
430}
431
432/*
433 * DAC volume attenuation mixer control
434 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100435static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200436{
437 int voices = kcontrol->private_value >> 8;
438 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
439 uinfo->count = voices;
440 uinfo->value.integer.min = 0; /* mute (-101dB) */
441 uinfo->value.integer.max = 0x7F; /* 0dB */
442 return 0;
443}
444
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100445static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200446{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100447 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200448 int i, ofs, voices;
449
450 voices = kcontrol->private_value >> 8;
451 ofs = kcontrol->private_value & 0xff;
452 for (i = 0; i < voices; i++)
453 ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
454 return 0;
455}
456
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100457static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200458{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100459 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200460 int i, idx, ofs, voices;
461 int change = 0;
462
463 voices = kcontrol->private_value >> 8;
464 ofs = kcontrol->private_value & 0xff;
465 snd_ice1712_save_gpio_status(ice);
466 for (i = 0; i < voices; i++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100467 unsigned int vol;
468 vol = ucontrol->value.integer.value[i];
469 if (vol > 0x7f)
470 continue;
471 vol |= ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE;
472 if (vol != ice->spec.phase28.vol[ofs+i]) {
473 ice->spec.phase28.vol[ofs+i] = vol;
474 idx = WM_DAC_ATTEN + ofs + i;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200475 wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
476 ice->spec.phase28.master[i]);
477 change = 1;
478 }
479 }
480 snd_ice1712_restore_gpio_status(ice);
481 return change;
482}
483
484/*
485 * WM8770 mute control
486 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100487static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200488 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
489 uinfo->count = kcontrol->private_value >> 8;
490 uinfo->value.integer.min = 0;
491 uinfo->value.integer.max = 1;
492 return 0;
493}
494
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100495static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200496{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100497 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200498 int voices, ofs, i;
499
500 voices = kcontrol->private_value >> 8;
501 ofs = kcontrol->private_value & 0xFF;
502
503 for (i = 0; i < voices; i++)
504 ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
505 return 0;
506}
507
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100508static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200509{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100510 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200511 int change = 0, voices, ofs, i;
512
513 voices = kcontrol->private_value >> 8;
514 ofs = kcontrol->private_value & 0xFF;
515
516 snd_ice1712_save_gpio_status(ice);
517 for (i = 0; i < voices; i++) {
518 int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
519 if (ucontrol->value.integer.value[i] != val) {
520 ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
521 ice->spec.phase28.vol[ofs + i] |=
522 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
523 wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
524 ice->spec.phase28.master[i]);
525 change = 1;
526 }
527 }
528 snd_ice1712_restore_gpio_status(ice);
529
530 return change;
531}
532
533/*
534 * WM8770 master mute control
535 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200536#define wm_master_mute_info snd_ctl_boolean_stereo_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200537
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100538static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200539{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100540 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200541
542 ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
543 ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
544 return 0;
545}
546
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100547static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200548{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100549 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200550 int change = 0, i;
551
552 snd_ice1712_save_gpio_status(ice);
553 for (i = 0; i < 2; i++) {
554 int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
555 if (ucontrol->value.integer.value[i] != val) {
556 int dac;
557 ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
558 ice->spec.phase28.master[i] |=
559 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
560 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
561 wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
562 ice->spec.phase28.vol[dac + i],
563 ice->spec.phase28.master[i]);
564 change = 1;
565 }
566 }
567 snd_ice1712_restore_gpio_status(ice);
568
569 return change;
570}
571
572/* digital master volume */
573#define PCM_0dB 0xff
574#define PCM_RES 128 /* -64dB */
575#define PCM_MIN (PCM_0dB - PCM_RES)
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100576static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200577{
578 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
579 uinfo->count = 1;
580 uinfo->value.integer.min = 0; /* mute (-64dB) */
581 uinfo->value.integer.max = PCM_RES; /* 0dB */
582 return 0;
583}
584
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100585static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200586{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100587 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200588 unsigned short val;
589
Ingo Molnar62932df2006-01-16 16:34:20 +0100590 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200591 val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
592 val = val > PCM_MIN ? (val - PCM_MIN) : 0;
593 ucontrol->value.integer.value[0] = val;
Ingo Molnar62932df2006-01-16 16:34:20 +0100594 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200595 return 0;
596}
597
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100598static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200599{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100600 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200601 unsigned short ovol, nvol;
602 int change = 0;
603
Simone Zinanniaed058e2005-04-11 14:08:40 +0200604 nvol = ucontrol->value.integer.value[0];
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100605 if (nvol > PCM_RES)
606 return -EINVAL;
607 snd_ice1712_save_gpio_status(ice);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200608 nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
609 ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
610 if (ovol != nvol) {
611 wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
612 wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
613 change = 1;
614 }
615 snd_ice1712_restore_gpio_status(ice);
616 return change;
617}
618
619/*
Simone Zinanniaed058e2005-04-11 14:08:40 +0200620 * Deemphasis
621 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200622#define phase28_deemp_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200623
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100624static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200625{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100626 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200627 ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
628 return 0;
629}
630
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100631static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200632{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100633 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200634 int temp, temp2;
635 temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
636 if (ucontrol->value.integer.value[0])
637 temp |= 0xf;
638 else
639 temp &= ~0xf;
640 if (temp != temp2) {
641 wm_put(ice, WM_DAC_CTRL2, temp);
642 return 1;
643 }
644 return 0;
645}
646
647/*
648 * ADC Oversampling
649 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100650static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200651{
652 static char *texts[2] = { "128x", "64x" };
653
654 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
655 uinfo->count = 1;
656 uinfo->value.enumerated.items = 2;
657
658 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
659 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
660 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
661
662 return 0;
663}
664
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100665static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200666{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100667 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200668 ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
669 return 0;
670}
671
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100672static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200673{
674 int temp, temp2;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100675 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200676
677 temp2 = temp = wm_get(ice, WM_MASTER);
678
679 if (ucontrol->value.enumerated.item[0])
680 temp |= 0x8;
681 else
682 temp &= ~0x8;
683
684 if (temp != temp2) {
685 wm_put(ice, WM_MASTER, temp);
686 return 1;
687 }
688 return 0;
689}
690
Takashi Iwai0cb29ea2007-01-29 15:33:49 +0100691static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
692static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
Takashi Iwaif640c322006-08-30 16:57:37 +0200693
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100694static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200695 {
696 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
697 .name = "Master Playback Switch",
698 .info = wm_master_mute_info,
699 .get = wm_master_mute_get,
700 .put = wm_master_mute_put
701 },
702 {
703 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200704 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
705 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200706 .name = "Master Playback Volume",
707 .info = wm_master_vol_info,
708 .get = wm_master_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200709 .put = wm_master_vol_put,
710 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200711 },
712 {
713 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
714 .name = "Front Playback Switch",
715 .info = wm_mute_info,
716 .get = wm_mute_get,
717 .put = wm_mute_put,
718 .private_value = (2 << 8) | 0
719 },
720 {
721 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200722 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
723 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200724 .name = "Front Playback Volume",
725 .info = wm_vol_info,
726 .get = wm_vol_get,
727 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200728 .private_value = (2 << 8) | 0,
729 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200730 },
731 {
732 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
733 .name = "Rear Playback Switch",
734 .info = wm_mute_info,
735 .get = wm_mute_get,
736 .put = wm_mute_put,
737 .private_value = (2 << 8) | 2
738 },
739 {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200741 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
742 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200743 .name = "Rear Playback Volume",
744 .info = wm_vol_info,
745 .get = wm_vol_get,
746 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200747 .private_value = (2 << 8) | 2,
748 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200749 },
750 {
751 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
752 .name = "Center Playback Switch",
753 .info = wm_mute_info,
754 .get = wm_mute_get,
755 .put = wm_mute_put,
756 .private_value = (1 << 8) | 4
757 },
758 {
759 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200760 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
761 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200762 .name = "Center Playback Volume",
763 .info = wm_vol_info,
764 .get = wm_vol_get,
765 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200766 .private_value = (1 << 8) | 4,
767 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200768 },
769 {
770 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
771 .name = "LFE Playback Switch",
772 .info = wm_mute_info,
773 .get = wm_mute_get,
774 .put = wm_mute_put,
775 .private_value = (1 << 8) | 5
776 },
777 {
778 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200779 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
780 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200781 .name = "LFE Playback Volume",
782 .info = wm_vol_info,
783 .get = wm_vol_get,
784 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200785 .private_value = (1 << 8) | 5,
786 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200787 },
788 {
789 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
790 .name = "Side Playback Switch",
791 .info = wm_mute_info,
792 .get = wm_mute_get,
793 .put = wm_mute_put,
794 .private_value = (2 << 8) | 6
795 },
796 {
797 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200798 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
799 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200800 .name = "Side Playback Volume",
801 .info = wm_vol_info,
802 .get = wm_vol_get,
803 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200804 .private_value = (2 << 8) | 6,
805 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200806 }
807};
808
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100809static struct snd_kcontrol_new wm_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200810 {
811 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
812 .name = "PCM Playback Switch",
813 .info = wm_pcm_mute_info,
814 .get = wm_pcm_mute_get,
815 .put = wm_pcm_mute_put
816 },
817 {
818 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200819 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
820 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200821 .name = "PCM Playback Volume",
822 .info = wm_pcm_vol_info,
823 .get = wm_pcm_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200824 .put = wm_pcm_vol_put,
825 .tlv = { .p = db_scale_wm_pcm }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200826 },
827 {
828 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
829 .name = "DAC Deemphasis Switch",
830 .info = phase28_deemp_info,
831 .get = phase28_deemp_get,
832 .put = phase28_deemp_put
833 },
834 {
835 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
836 .name = "ADC Oversampling",
837 .info = phase28_oversampling_info,
838 .get = phase28_oversampling_get,
839 .put = phase28_oversampling_put
840 }
841};
842
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100843static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200844{
845 unsigned int i, counts;
846 int err;
847
848 counts = ARRAY_SIZE(phase28_dac_controls);
849 for (i = 0; i < counts; i++) {
850 err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
851 if (err < 0)
852 return err;
853 }
854
855 for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
856 err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
857 if (err < 0)
858 return err;
859 }
860
861 return 0;
862}
863
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100864struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 {
866 .subvendor = VT1724_SUBDEVICE_PHASE22,
867 .name = "Terratec PHASE 22",
868 .model = "phase22",
869 .chip_init = phase22_init,
870 .build_controls = phase22_add_controls,
871 .eeprom_size = sizeof(phase22_eeprom),
872 .eeprom_data = phase22_eeprom,
873 },
Simone Zinanniaed058e2005-04-11 14:08:40 +0200874 {
875 .subvendor = VT1724_SUBDEVICE_PHASE28,
876 .name = "Terratec PHASE 28",
877 .model = "phase28",
878 .chip_init = phase28_init,
879 .build_controls = phase28_add_controls,
880 .eeprom_size = sizeof(phase28_eeprom),
881 .eeprom_data = phase28_eeprom,
882 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 { } /* terminator */
884};