blob: c81efc2f8c95ec39231e9aa0bebcbea230cb7dde [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
36#include <sound/driver.h>
37#include <asm/io.h>
38#include <linux/delay.h>
39#include <linux/interrupt.h>
40#include <linux/init.h>
41#include <linux/slab.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010042#include <linux/mutex.h>
43
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <sound/core.h>
45
46#include "ice1712.h"
47#include "envy24ht.h"
48#include "phase.h"
Takashi Iwaif640c322006-08-30 16:57:37 +020049#include <sound/tlv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Simone Zinanniaed058e2005-04-11 14:08:40 +020051/* WM8770 registers */
52#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
53#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
54#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
55#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */
56#define WM_PHASE_SWAP 0x12 /* DAC phase */
57#define WM_DAC_CTRL1 0x13 /* DAC control bits */
58#define WM_MUTE 0x14 /* mute controls */
59#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
60#define WM_INT_CTRL 0x16 /* interface control */
61#define WM_MASTER 0x17 /* master clock and mode */
62#define WM_POWERDOWN 0x18 /* power-down controls */
63#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
64#define WM_ADC_MUX 0x1b /* input MUX */
65#define WM_OUT_MUX1 0x1c /* output MUX */
66#define WM_OUT_MUX2 0x1e /* output MUX */
67#define WM_RESET 0x1f /* software reset */
68
69
70/*
71 * Logarithmic volume values for WM8770
72 * Computed as 20 * Log10(255 / x)
73 */
Takashi Iwai32b47da2007-01-29 15:26:36 +010074static const unsigned char wm_vol[256] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +020075 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
76 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
77 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
78 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
79 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
80 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,
81 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,
82 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,
83 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,
84 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,
85 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,
86 0, 0
87};
88
89#define WM_VOL_MAX (sizeof(wm_vol) - 1)
90#define WM_VOL_MUTE 0x8000
91
Takashi Iwai1b60f6b2007-03-13 22:13:47 +010092static struct snd_akm4xxx akm_phase22 __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 .type = SND_AK4524,
94 .num_dacs = 2,
95 .num_adcs = 2,
96};
97
Takashi Iwai1b60f6b2007-03-13 22:13:47 +010098static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 .caddr = 2,
100 .cif = 1,
101 .data_mask = 1 << 4,
102 .clk_mask = 1 << 5,
103 .cs_mask = 1 << 10,
104 .cs_addr = 1 << 10,
105 .cs_none = 0,
106 .add_flags = 1 << 3,
107 .mask_flags = 0,
108};
109
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100110static int __devinit phase22_init(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100112 struct snd_akm4xxx *ak;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 int err;
114
115 // Configure DAC/ADC description for generic part of ice1724
116 switch (ice->eeprom.subvendor) {
117 case VT1724_SUBDEVICE_PHASE22:
118 ice->num_total_dacs = 2;
119 ice->num_total_adcs = 2;
120 ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
121 break;
122 default:
123 snd_BUG();
124 return -EINVAL;
125 }
126
127 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100128 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 if (! ak)
130 return -ENOMEM;
131 ice->akm_codecs = 1;
132 switch (ice->eeprom.subvendor) {
133 case VT1724_SUBDEVICE_PHASE22:
134 if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
135 return err;
136 break;
137 }
138
139 return 0;
140}
141
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100142static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
144 int err = 0;
145
146 switch (ice->eeprom.subvendor) {
147 case VT1724_SUBDEVICE_PHASE22:
148 err = snd_ice1712_akm4xxx_build_controls(ice);
149 if (err < 0)
150 return err;
151 }
152 return 0;
153}
154
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100155static unsigned char phase22_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +0100156 [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */
157 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
158 [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */
159 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
160 [ICE_EEP2_GPIO_DIR] = 0xff,
161 [ICE_EEP2_GPIO_DIR1] = 0xff,
162 [ICE_EEP2_GPIO_DIR2] = 0xff,
163 [ICE_EEP2_GPIO_MASK] = 0x00,
164 [ICE_EEP2_GPIO_MASK1] = 0x00,
165 [ICE_EEP2_GPIO_MASK2] = 0x00,
166 [ICE_EEP2_GPIO_STATE] = 0x00,
167 [ICE_EEP2_GPIO_STATE1] = 0x00,
168 [ICE_EEP2_GPIO_STATE2] = 0x00,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169};
170
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100171static unsigned char phase28_eeprom[] __devinitdata = {
Takashi Iwai189bc172007-01-29 15:25:40 +0100172 [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
173 [ICE_EEP2_ACLINK] = 0x80, /* I2S */
174 [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
175 [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
176 [ICE_EEP2_GPIO_DIR] = 0xff,
177 [ICE_EEP2_GPIO_DIR1] = 0xff,
178 [ICE_EEP2_GPIO_DIR2] = 0x5f,
179 [ICE_EEP2_GPIO_MASK] = 0x00,
180 [ICE_EEP2_GPIO_MASK1] = 0x00,
181 [ICE_EEP2_GPIO_MASK2] = 0x00,
182 [ICE_EEP2_GPIO_STATE] = 0x00,
183 [ICE_EEP2_GPIO_STATE1] = 0x00,
184 [ICE_EEP2_GPIO_STATE2] = 0x00,
Simone Zinanniaed058e2005-04-11 14:08:40 +0200185};
186
187/*
188 * write data in the SPI mode
189 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100190static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200191{
192 unsigned int tmp;
193 int i;
194
195 tmp = snd_ice1712_gpio_read(ice);
196
197 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
198 PHASE28_WM_CS));
199 tmp |= PHASE28_WM_RW;
200 tmp &= ~cs;
201 snd_ice1712_gpio_write(ice, tmp);
202 udelay(1);
203
204 for (i = bits - 1; i >= 0; i--) {
205 tmp &= ~PHASE28_SPI_CLK;
206 snd_ice1712_gpio_write(ice, tmp);
207 udelay(1);
208 if (data & (1 << i))
209 tmp |= PHASE28_SPI_MOSI;
210 else
211 tmp &= ~PHASE28_SPI_MOSI;
212 snd_ice1712_gpio_write(ice, tmp);
213 udelay(1);
214 tmp |= PHASE28_SPI_CLK;
215 snd_ice1712_gpio_write(ice, tmp);
216 udelay(1);
217 }
218
219 tmp &= ~PHASE28_SPI_CLK;
220 tmp |= cs;
221 snd_ice1712_gpio_write(ice, tmp);
222 udelay(1);
223 tmp |= PHASE28_SPI_CLK;
224 snd_ice1712_gpio_write(ice, tmp);
225 udelay(1);
226}
227
228/*
229 * get the current register value of WM codec
230 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100231static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200232{
233 reg <<= 1;
234 return ((unsigned short)ice->akm[0].images[reg] << 8) |
235 ice->akm[0].images[reg + 1];
236}
237
238/*
239 * set the register value of WM codec
240 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100241static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200242{
243 phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
244}
245
246/*
247 * set the register value of WM codec and remember it
248 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100249static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200250{
251 wm_put_nocache(ice, reg, val);
252 reg <<= 1;
253 ice->akm[0].images[reg] = val >> 8;
254 ice->akm[0].images[reg + 1] = val;
255}
256
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100257static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200258{
259 unsigned char nvol;
260
261 if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
262 nvol = 0;
263 else
264 nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
265
266 wm_put(ice, index, nvol);
267 wm_put_nocache(ice, index, 0x180 | nvol);
268}
269
270/*
271 * DAC mute control
272 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200273#define wm_pcm_mute_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200274
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100275static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200276{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100277 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200278
Ingo Molnar62932df2006-01-16 16:34:20 +0100279 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200280 ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
Ingo Molnar62932df2006-01-16 16:34:20 +0100281 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200282 return 0;
283}
284
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100285static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200286{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100287 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200288 unsigned short nval, oval;
289 int change;
290
291 snd_ice1712_save_gpio_status(ice);
292 oval = wm_get(ice, WM_MUTE);
293 nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
294 if ((change = (nval != oval)))
295 wm_put(ice, WM_MUTE, nval);
296 snd_ice1712_restore_gpio_status(ice);
297
298 return change;
299}
300
301/*
302 * Master volume attenuation mixer control
303 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100304static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200305{
306 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
307 uinfo->count = 2;
308 uinfo->value.integer.min = 0;
309 uinfo->value.integer.max = WM_VOL_MAX;
310 return 0;
311}
312
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100313static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200314{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100315 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200316 int i;
317 for (i=0; i<2; i++)
318 ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
319 return 0;
320}
321
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100322static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200323{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100324 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200325 int ch, change = 0;
326
327 snd_ice1712_save_gpio_status(ice);
328 for (ch = 0; ch < 2; ch++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100329 unsigned int vol = ucontrol->value.integer.value[ch];
330 if (vol > WM_VOL_MAX)
331 continue;
332 vol |= ice->spec.phase28.master[ch] & WM_VOL_MUTE;
333 if (vol != ice->spec.phase28.master[ch]) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200334 int dac;
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100335 ice->spec.phase28.master[ch] = vol;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200336 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
337 wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
338 ice->spec.phase28.vol[dac + ch],
339 ice->spec.phase28.master[ch]);
340 change = 1;
341 }
342 }
343 snd_ice1712_restore_gpio_status(ice);
344 return change;
345}
346
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100347static int __devinit phase28_init(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200348{
Takashi Iwai32b47da2007-01-29 15:26:36 +0100349 static const unsigned short wm_inits_phase28[] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200350 /* These come first to reduce init pop noise */
351 0x1b, 0x044, /* ADC Mux (AC'97 source) */
352 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
353 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
354
355 0x18, 0x000, /* All power-up */
356
357 0x16, 0x122, /* I2S, normal polarity, 24bit */
358 0x17, 0x022, /* 256fs, slave mode */
359 0x00, 0, /* DAC1 analog mute */
360 0x01, 0, /* DAC2 analog mute */
361 0x02, 0, /* DAC3 analog mute */
362 0x03, 0, /* DAC4 analog mute */
363 0x04, 0, /* DAC5 analog mute */
364 0x05, 0, /* DAC6 analog mute */
365 0x06, 0, /* DAC7 analog mute */
366 0x07, 0, /* DAC8 analog mute */
367 0x08, 0x100, /* master analog mute */
368 0x09, 0xff, /* DAC1 digital full */
369 0x0a, 0xff, /* DAC2 digital full */
370 0x0b, 0xff, /* DAC3 digital full */
371 0x0c, 0xff, /* DAC4 digital full */
372 0x0d, 0xff, /* DAC5 digital full */
373 0x0e, 0xff, /* DAC6 digital full */
374 0x0f, 0xff, /* DAC7 digital full */
375 0x10, 0xff, /* DAC8 digital full */
376 0x11, 0x1ff, /* master digital full */
377 0x12, 0x000, /* phase normal */
378 0x13, 0x090, /* unmute DAC L/R */
379 0x14, 0x000, /* all unmute */
380 0x15, 0x000, /* no deemphasis, no ZFLG */
381 0x19, 0x000, /* -12dB ADC/L */
382 0x1a, 0x000, /* -12dB ADC/R */
383 (unsigned short)-1
384 };
385
386 unsigned int tmp;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100387 struct snd_akm4xxx *ak;
Takashi Iwai32b47da2007-01-29 15:26:36 +0100388 const unsigned short *p;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200389 int i;
390
391 ice->num_total_dacs = 8;
392 ice->num_total_adcs = 2;
393
394 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100395 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200396 if (!ak)
397 return -ENOMEM;
398 ice->akm_codecs = 1;
399
400 snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
401
402 /* reset the wm codec as the SPI mode */
403 snd_ice1712_save_gpio_status(ice);
404 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
405
406 tmp = snd_ice1712_gpio_read(ice);
407 tmp &= ~PHASE28_WM_RESET;
408 snd_ice1712_gpio_write(ice, tmp);
409 udelay(1);
410 tmp |= PHASE28_WM_CS;
411 snd_ice1712_gpio_write(ice, tmp);
412 udelay(1);
413 tmp |= PHASE28_WM_RESET;
414 snd_ice1712_gpio_write(ice, tmp);
415 udelay(1);
416
417 p = wm_inits_phase28;
418 for (; *p != (unsigned short)-1; p += 2)
419 wm_put(ice, p[0], p[1]);
420
421 snd_ice1712_restore_gpio_status(ice);
422
423 ice->spec.phase28.master[0] = WM_VOL_MUTE;
424 ice->spec.phase28.master[1] = WM_VOL_MUTE;
425 for (i = 0; i < ice->num_total_dacs; i++) {
426 ice->spec.phase28.vol[i] = WM_VOL_MUTE;
427 wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
428 }
429
430 return 0;
431}
432
433/*
434 * DAC volume attenuation mixer control
435 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100436static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200437{
438 int voices = kcontrol->private_value >> 8;
439 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
440 uinfo->count = voices;
441 uinfo->value.integer.min = 0; /* mute (-101dB) */
442 uinfo->value.integer.max = 0x7F; /* 0dB */
443 return 0;
444}
445
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100446static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200447{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100448 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200449 int i, ofs, voices;
450
451 voices = kcontrol->private_value >> 8;
452 ofs = kcontrol->private_value & 0xff;
453 for (i = 0; i < voices; i++)
454 ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
455 return 0;
456}
457
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100458static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200459{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100460 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200461 int i, idx, ofs, voices;
462 int change = 0;
463
464 voices = kcontrol->private_value >> 8;
465 ofs = kcontrol->private_value & 0xff;
466 snd_ice1712_save_gpio_status(ice);
467 for (i = 0; i < voices; i++) {
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100468 unsigned int vol;
469 vol = ucontrol->value.integer.value[i];
470 if (vol > 0x7f)
471 continue;
472 vol |= ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE;
473 if (vol != ice->spec.phase28.vol[ofs+i]) {
474 ice->spec.phase28.vol[ofs+i] = vol;
475 idx = WM_DAC_ATTEN + ofs + i;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200476 wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
477 ice->spec.phase28.master[i]);
478 change = 1;
479 }
480 }
481 snd_ice1712_restore_gpio_status(ice);
482 return change;
483}
484
485/*
486 * WM8770 mute control
487 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100488static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200489 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
490 uinfo->count = kcontrol->private_value >> 8;
491 uinfo->value.integer.min = 0;
492 uinfo->value.integer.max = 1;
493 return 0;
494}
495
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100496static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200497{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100498 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200499 int voices, ofs, i;
500
501 voices = kcontrol->private_value >> 8;
502 ofs = kcontrol->private_value & 0xFF;
503
504 for (i = 0; i < voices; i++)
505 ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
506 return 0;
507}
508
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100509static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200510{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100511 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200512 int change = 0, voices, ofs, i;
513
514 voices = kcontrol->private_value >> 8;
515 ofs = kcontrol->private_value & 0xFF;
516
517 snd_ice1712_save_gpio_status(ice);
518 for (i = 0; i < voices; i++) {
519 int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
520 if (ucontrol->value.integer.value[i] != val) {
521 ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
522 ice->spec.phase28.vol[ofs + i] |=
523 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
524 wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
525 ice->spec.phase28.master[i]);
526 change = 1;
527 }
528 }
529 snd_ice1712_restore_gpio_status(ice);
530
531 return change;
532}
533
534/*
535 * WM8770 master mute control
536 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200537#define wm_master_mute_info snd_ctl_boolean_stereo_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200538
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100539static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200540{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100541 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200542
543 ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
544 ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
545 return 0;
546}
547
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100548static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200549{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100550 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200551 int change = 0, i;
552
553 snd_ice1712_save_gpio_status(ice);
554 for (i = 0; i < 2; i++) {
555 int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
556 if (ucontrol->value.integer.value[i] != val) {
557 int dac;
558 ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
559 ice->spec.phase28.master[i] |=
560 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
561 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
562 wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
563 ice->spec.phase28.vol[dac + i],
564 ice->spec.phase28.master[i]);
565 change = 1;
566 }
567 }
568 snd_ice1712_restore_gpio_status(ice);
569
570 return change;
571}
572
573/* digital master volume */
574#define PCM_0dB 0xff
575#define PCM_RES 128 /* -64dB */
576#define PCM_MIN (PCM_0dB - PCM_RES)
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100577static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200578{
579 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
580 uinfo->count = 1;
581 uinfo->value.integer.min = 0; /* mute (-64dB) */
582 uinfo->value.integer.max = PCM_RES; /* 0dB */
583 return 0;
584}
585
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100586static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200587{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100588 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200589 unsigned short val;
590
Ingo Molnar62932df2006-01-16 16:34:20 +0100591 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200592 val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
593 val = val > PCM_MIN ? (val - PCM_MIN) : 0;
594 ucontrol->value.integer.value[0] = val;
Ingo Molnar62932df2006-01-16 16:34:20 +0100595 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200596 return 0;
597}
598
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100599static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200600{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100601 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200602 unsigned short ovol, nvol;
603 int change = 0;
604
Simone Zinanniaed058e2005-04-11 14:08:40 +0200605 nvol = ucontrol->value.integer.value[0];
Takashi Iwai9cd17cd2007-11-15 15:56:07 +0100606 if (nvol > PCM_RES)
607 return -EINVAL;
608 snd_ice1712_save_gpio_status(ice);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200609 nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
610 ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
611 if (ovol != nvol) {
612 wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
613 wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
614 change = 1;
615 }
616 snd_ice1712_restore_gpio_status(ice);
617 return change;
618}
619
620/*
Simone Zinanniaed058e2005-04-11 14:08:40 +0200621 * Deemphasis
622 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200623#define phase28_deemp_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200624
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100625static int phase28_deemp_get(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 ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
629 return 0;
630}
631
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100632static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200633{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100634 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200635 int temp, temp2;
636 temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
637 if (ucontrol->value.integer.value[0])
638 temp |= 0xf;
639 else
640 temp &= ~0xf;
641 if (temp != temp2) {
642 wm_put(ice, WM_DAC_CTRL2, temp);
643 return 1;
644 }
645 return 0;
646}
647
648/*
649 * ADC Oversampling
650 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100651static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200652{
653 static char *texts[2] = { "128x", "64x" };
654
655 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
656 uinfo->count = 1;
657 uinfo->value.enumerated.items = 2;
658
659 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
660 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
661 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
662
663 return 0;
664}
665
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100666static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200667{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100668 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200669 ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
670 return 0;
671}
672
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100673static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200674{
675 int temp, temp2;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100676 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200677
678 temp2 = temp = wm_get(ice, WM_MASTER);
679
680 if (ucontrol->value.enumerated.item[0])
681 temp |= 0x8;
682 else
683 temp &= ~0x8;
684
685 if (temp != temp2) {
686 wm_put(ice, WM_MASTER, temp);
687 return 1;
688 }
689 return 0;
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);
Takashi Iwaif640c322006-08-30 16:57:37 +0200694
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100695static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200696 {
697 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
698 .name = "Master Playback Switch",
699 .info = wm_master_mute_info,
700 .get = wm_master_mute_get,
701 .put = wm_master_mute_put
702 },
703 {
704 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200705 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
706 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200707 .name = "Master Playback Volume",
708 .info = wm_master_vol_info,
709 .get = wm_master_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200710 .put = wm_master_vol_put,
711 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200712 },
713 {
714 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
715 .name = "Front Playback Switch",
716 .info = wm_mute_info,
717 .get = wm_mute_get,
718 .put = wm_mute_put,
719 .private_value = (2 << 8) | 0
720 },
721 {
722 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200723 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
724 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200725 .name = "Front Playback Volume",
726 .info = wm_vol_info,
727 .get = wm_vol_get,
728 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200729 .private_value = (2 << 8) | 0,
730 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200731 },
732 {
733 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
734 .name = "Rear Playback Switch",
735 .info = wm_mute_info,
736 .get = wm_mute_get,
737 .put = wm_mute_put,
738 .private_value = (2 << 8) | 2
739 },
740 {
741 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200742 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
743 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200744 .name = "Rear Playback Volume",
745 .info = wm_vol_info,
746 .get = wm_vol_get,
747 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200748 .private_value = (2 << 8) | 2,
749 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200750 },
751 {
752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
753 .name = "Center Playback Switch",
754 .info = wm_mute_info,
755 .get = wm_mute_get,
756 .put = wm_mute_put,
757 .private_value = (1 << 8) | 4
758 },
759 {
760 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200761 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
762 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200763 .name = "Center Playback Volume",
764 .info = wm_vol_info,
765 .get = wm_vol_get,
766 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200767 .private_value = (1 << 8) | 4,
768 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200769 },
770 {
771 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
772 .name = "LFE Playback Switch",
773 .info = wm_mute_info,
774 .get = wm_mute_get,
775 .put = wm_mute_put,
776 .private_value = (1 << 8) | 5
777 },
778 {
779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200780 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
781 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200782 .name = "LFE Playback Volume",
783 .info = wm_vol_info,
784 .get = wm_vol_get,
785 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200786 .private_value = (1 << 8) | 5,
787 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200788 },
789 {
790 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
791 .name = "Side Playback Switch",
792 .info = wm_mute_info,
793 .get = wm_mute_get,
794 .put = wm_mute_put,
795 .private_value = (2 << 8) | 6
796 },
797 {
798 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200799 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
800 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200801 .name = "Side Playback Volume",
802 .info = wm_vol_info,
803 .get = wm_vol_get,
804 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200805 .private_value = (2 << 8) | 6,
806 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200807 }
808};
809
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100810static struct snd_kcontrol_new wm_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200811 {
812 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
813 .name = "PCM Playback Switch",
814 .info = wm_pcm_mute_info,
815 .get = wm_pcm_mute_get,
816 .put = wm_pcm_mute_put
817 },
818 {
819 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200820 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
821 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200822 .name = "PCM Playback Volume",
823 .info = wm_pcm_vol_info,
824 .get = wm_pcm_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200825 .put = wm_pcm_vol_put,
826 .tlv = { .p = db_scale_wm_pcm }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200827 },
828 {
829 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
830 .name = "DAC Deemphasis Switch",
831 .info = phase28_deemp_info,
832 .get = phase28_deemp_get,
833 .put = phase28_deemp_put
834 },
835 {
836 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
837 .name = "ADC Oversampling",
838 .info = phase28_oversampling_info,
839 .get = phase28_oversampling_get,
840 .put = phase28_oversampling_put
841 }
842};
843
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100844static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200845{
846 unsigned int i, counts;
847 int err;
848
849 counts = ARRAY_SIZE(phase28_dac_controls);
850 for (i = 0; i < counts; i++) {
851 err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
852 if (err < 0)
853 return err;
854 }
855
856 for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
857 err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
858 if (err < 0)
859 return err;
860 }
861
862 return 0;
863}
864
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100865struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 {
867 .subvendor = VT1724_SUBDEVICE_PHASE22,
868 .name = "Terratec PHASE 22",
869 .model = "phase22",
870 .chip_init = phase22_init,
871 .build_controls = phase22_add_controls,
872 .eeprom_size = sizeof(phase22_eeprom),
873 .eeprom_data = phase22_eeprom,
874 },
Simone Zinanniaed058e2005-04-11 14:08:40 +0200875 {
876 .subvendor = VT1724_SUBDEVICE_PHASE28,
877 .name = "Terratec PHASE 28",
878 .model = "phase28",
879 .chip_init = phase28_init,
880 .build_controls = phase28_add_controls,
881 .eeprom_size = sizeof(phase28_eeprom),
882 .eeprom_data = phase28_eeprom,
883 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 { } /* terminator */
885};