blob: 3ac25058bb588fa583afc3bcc0a5b21ec577b6d7 [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++) {
329 if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) {
330 int dac;
331 ice->spec.phase28.master[ch] &= WM_VOL_MUTE;
332 ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch];
333 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
334 wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
335 ice->spec.phase28.vol[dac + ch],
336 ice->spec.phase28.master[ch]);
337 change = 1;
338 }
339 }
340 snd_ice1712_restore_gpio_status(ice);
341 return change;
342}
343
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100344static int __devinit phase28_init(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200345{
Takashi Iwai32b47da2007-01-29 15:26:36 +0100346 static const unsigned short wm_inits_phase28[] = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200347 /* These come first to reduce init pop noise */
348 0x1b, 0x044, /* ADC Mux (AC'97 source) */
349 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
350 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
351
352 0x18, 0x000, /* All power-up */
353
354 0x16, 0x122, /* I2S, normal polarity, 24bit */
355 0x17, 0x022, /* 256fs, slave mode */
356 0x00, 0, /* DAC1 analog mute */
357 0x01, 0, /* DAC2 analog mute */
358 0x02, 0, /* DAC3 analog mute */
359 0x03, 0, /* DAC4 analog mute */
360 0x04, 0, /* DAC5 analog mute */
361 0x05, 0, /* DAC6 analog mute */
362 0x06, 0, /* DAC7 analog mute */
363 0x07, 0, /* DAC8 analog mute */
364 0x08, 0x100, /* master analog mute */
365 0x09, 0xff, /* DAC1 digital full */
366 0x0a, 0xff, /* DAC2 digital full */
367 0x0b, 0xff, /* DAC3 digital full */
368 0x0c, 0xff, /* DAC4 digital full */
369 0x0d, 0xff, /* DAC5 digital full */
370 0x0e, 0xff, /* DAC6 digital full */
371 0x0f, 0xff, /* DAC7 digital full */
372 0x10, 0xff, /* DAC8 digital full */
373 0x11, 0x1ff, /* master digital full */
374 0x12, 0x000, /* phase normal */
375 0x13, 0x090, /* unmute DAC L/R */
376 0x14, 0x000, /* all unmute */
377 0x15, 0x000, /* no deemphasis, no ZFLG */
378 0x19, 0x000, /* -12dB ADC/L */
379 0x1a, 0x000, /* -12dB ADC/R */
380 (unsigned short)-1
381 };
382
383 unsigned int tmp;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100384 struct snd_akm4xxx *ak;
Takashi Iwai32b47da2007-01-29 15:26:36 +0100385 const unsigned short *p;
Simone Zinanniaed058e2005-04-11 14:08:40 +0200386 int i;
387
388 ice->num_total_dacs = 8;
389 ice->num_total_adcs = 2;
390
391 // Initialize analog chips
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100392 ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200393 if (!ak)
394 return -ENOMEM;
395 ice->akm_codecs = 1;
396
397 snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
398
399 /* reset the wm codec as the SPI mode */
400 snd_ice1712_save_gpio_status(ice);
401 snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
402
403 tmp = snd_ice1712_gpio_read(ice);
404 tmp &= ~PHASE28_WM_RESET;
405 snd_ice1712_gpio_write(ice, tmp);
406 udelay(1);
407 tmp |= PHASE28_WM_CS;
408 snd_ice1712_gpio_write(ice, tmp);
409 udelay(1);
410 tmp |= PHASE28_WM_RESET;
411 snd_ice1712_gpio_write(ice, tmp);
412 udelay(1);
413
414 p = wm_inits_phase28;
415 for (; *p != (unsigned short)-1; p += 2)
416 wm_put(ice, p[0], p[1]);
417
418 snd_ice1712_restore_gpio_status(ice);
419
420 ice->spec.phase28.master[0] = WM_VOL_MUTE;
421 ice->spec.phase28.master[1] = WM_VOL_MUTE;
422 for (i = 0; i < ice->num_total_dacs; i++) {
423 ice->spec.phase28.vol[i] = WM_VOL_MUTE;
424 wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
425 }
426
427 return 0;
428}
429
430/*
431 * DAC volume attenuation mixer control
432 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100433static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200434{
435 int voices = kcontrol->private_value >> 8;
436 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
437 uinfo->count = voices;
438 uinfo->value.integer.min = 0; /* mute (-101dB) */
439 uinfo->value.integer.max = 0x7F; /* 0dB */
440 return 0;
441}
442
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100443static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200444{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100445 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200446 int i, ofs, voices;
447
448 voices = kcontrol->private_value >> 8;
449 ofs = kcontrol->private_value & 0xff;
450 for (i = 0; i < voices; i++)
451 ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
452 return 0;
453}
454
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100455static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200456{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100457 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200458 int i, idx, ofs, voices;
459 int change = 0;
460
461 voices = kcontrol->private_value >> 8;
462 ofs = kcontrol->private_value & 0xff;
463 snd_ice1712_save_gpio_status(ice);
464 for (i = 0; i < voices; i++) {
465 idx = WM_DAC_ATTEN + ofs + i;
466 if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) {
467 ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE;
468 ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i];
469 wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
470 ice->spec.phase28.master[i]);
471 change = 1;
472 }
473 }
474 snd_ice1712_restore_gpio_status(ice);
475 return change;
476}
477
478/*
479 * WM8770 mute control
480 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100481static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200482 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
483 uinfo->count = kcontrol->private_value >> 8;
484 uinfo->value.integer.min = 0;
485 uinfo->value.integer.max = 1;
486 return 0;
487}
488
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100489static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200490{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100491 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200492 int voices, ofs, i;
493
494 voices = kcontrol->private_value >> 8;
495 ofs = kcontrol->private_value & 0xFF;
496
497 for (i = 0; i < voices; i++)
498 ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
499 return 0;
500}
501
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100502static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200503{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100504 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200505 int change = 0, voices, ofs, i;
506
507 voices = kcontrol->private_value >> 8;
508 ofs = kcontrol->private_value & 0xFF;
509
510 snd_ice1712_save_gpio_status(ice);
511 for (i = 0; i < voices; i++) {
512 int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
513 if (ucontrol->value.integer.value[i] != val) {
514 ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
515 ice->spec.phase28.vol[ofs + i] |=
516 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
517 wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
518 ice->spec.phase28.master[i]);
519 change = 1;
520 }
521 }
522 snd_ice1712_restore_gpio_status(ice);
523
524 return change;
525}
526
527/*
528 * WM8770 master mute control
529 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200530#define wm_master_mute_info snd_ctl_boolean_stereo_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200531
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100532static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200533{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100534 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200535
536 ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
537 ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
538 return 0;
539}
540
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100541static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200542{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100543 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200544 int change = 0, i;
545
546 snd_ice1712_save_gpio_status(ice);
547 for (i = 0; i < 2; i++) {
548 int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
549 if (ucontrol->value.integer.value[i] != val) {
550 int dac;
551 ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
552 ice->spec.phase28.master[i] |=
553 ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
554 for (dac = 0; dac < ice->num_total_dacs; dac += 2)
555 wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
556 ice->spec.phase28.vol[dac + i],
557 ice->spec.phase28.master[i]);
558 change = 1;
559 }
560 }
561 snd_ice1712_restore_gpio_status(ice);
562
563 return change;
564}
565
566/* digital master volume */
567#define PCM_0dB 0xff
568#define PCM_RES 128 /* -64dB */
569#define PCM_MIN (PCM_0dB - PCM_RES)
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100570static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200571{
572 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
573 uinfo->count = 1;
574 uinfo->value.integer.min = 0; /* mute (-64dB) */
575 uinfo->value.integer.max = PCM_RES; /* 0dB */
576 return 0;
577}
578
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100579static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200580{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100581 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200582 unsigned short val;
583
Ingo Molnar62932df2006-01-16 16:34:20 +0100584 mutex_lock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200585 val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
586 val = val > PCM_MIN ? (val - PCM_MIN) : 0;
587 ucontrol->value.integer.value[0] = val;
Ingo Molnar62932df2006-01-16 16:34:20 +0100588 mutex_unlock(&ice->gpio_mutex);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200589 return 0;
590}
591
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100592static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200593{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100594 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200595 unsigned short ovol, nvol;
596 int change = 0;
597
598 snd_ice1712_save_gpio_status(ice);
599 nvol = ucontrol->value.integer.value[0];
600 nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
601 ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
602 if (ovol != nvol) {
603 wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
604 wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
605 change = 1;
606 }
607 snd_ice1712_restore_gpio_status(ice);
608 return change;
609}
610
611/*
Simone Zinanniaed058e2005-04-11 14:08:40 +0200612 * Deemphasis
613 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200614#define phase28_deemp_info snd_ctl_boolean_mono_info
Simone Zinanniaed058e2005-04-11 14:08:40 +0200615
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100616static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200617{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100618 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200619 ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
620 return 0;
621}
622
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100623static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200624{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100625 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200626 int temp, temp2;
627 temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
628 if (ucontrol->value.integer.value[0])
629 temp |= 0xf;
630 else
631 temp &= ~0xf;
632 if (temp != temp2) {
633 wm_put(ice, WM_DAC_CTRL2, temp);
634 return 1;
635 }
636 return 0;
637}
638
639/*
640 * ADC Oversampling
641 */
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100642static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200643{
644 static char *texts[2] = { "128x", "64x" };
645
646 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
647 uinfo->count = 1;
648 uinfo->value.enumerated.items = 2;
649
650 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
651 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
652 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
653
654 return 0;
655}
656
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100657static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200658{
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100659 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200660 ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
661 return 0;
662}
663
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100664static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200665{
666 int temp, temp2;
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100667 struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
Simone Zinanniaed058e2005-04-11 14:08:40 +0200668
669 temp2 = temp = wm_get(ice, WM_MASTER);
670
671 if (ucontrol->value.enumerated.item[0])
672 temp |= 0x8;
673 else
674 temp &= ~0x8;
675
676 if (temp != temp2) {
677 wm_put(ice, WM_MASTER, temp);
678 return 1;
679 }
680 return 0;
681}
682
Takashi Iwai0cb29ea2007-01-29 15:33:49 +0100683static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
684static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
Takashi Iwaif640c322006-08-30 16:57:37 +0200685
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100686static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200687 {
688 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
689 .name = "Master Playback Switch",
690 .info = wm_master_mute_info,
691 .get = wm_master_mute_get,
692 .put = wm_master_mute_put
693 },
694 {
695 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200696 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
697 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200698 .name = "Master Playback Volume",
699 .info = wm_master_vol_info,
700 .get = wm_master_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200701 .put = wm_master_vol_put,
702 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200703 },
704 {
705 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
706 .name = "Front Playback Switch",
707 .info = wm_mute_info,
708 .get = wm_mute_get,
709 .put = wm_mute_put,
710 .private_value = (2 << 8) | 0
711 },
712 {
713 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200714 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
715 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200716 .name = "Front Playback Volume",
717 .info = wm_vol_info,
718 .get = wm_vol_get,
719 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200720 .private_value = (2 << 8) | 0,
721 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200722 },
723 {
724 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
725 .name = "Rear Playback Switch",
726 .info = wm_mute_info,
727 .get = wm_mute_get,
728 .put = wm_mute_put,
729 .private_value = (2 << 8) | 2
730 },
731 {
732 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200733 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
734 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200735 .name = "Rear Playback Volume",
736 .info = wm_vol_info,
737 .get = wm_vol_get,
738 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200739 .private_value = (2 << 8) | 2,
740 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200741 },
742 {
743 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
744 .name = "Center Playback Switch",
745 .info = wm_mute_info,
746 .get = wm_mute_get,
747 .put = wm_mute_put,
748 .private_value = (1 << 8) | 4
749 },
750 {
751 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200752 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
753 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200754 .name = "Center Playback Volume",
755 .info = wm_vol_info,
756 .get = wm_vol_get,
757 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200758 .private_value = (1 << 8) | 4,
759 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200760 },
761 {
762 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
763 .name = "LFE Playback Switch",
764 .info = wm_mute_info,
765 .get = wm_mute_get,
766 .put = wm_mute_put,
767 .private_value = (1 << 8) | 5
768 },
769 {
770 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200771 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
772 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200773 .name = "LFE Playback Volume",
774 .info = wm_vol_info,
775 .get = wm_vol_get,
776 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200777 .private_value = (1 << 8) | 5,
778 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200779 },
780 {
781 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
782 .name = "Side Playback Switch",
783 .info = wm_mute_info,
784 .get = wm_mute_get,
785 .put = wm_mute_put,
786 .private_value = (2 << 8) | 6
787 },
788 {
789 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200790 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
791 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200792 .name = "Side Playback Volume",
793 .info = wm_vol_info,
794 .get = wm_vol_get,
795 .put = wm_vol_put,
Takashi Iwaif640c322006-08-30 16:57:37 +0200796 .private_value = (2 << 8) | 6,
797 .tlv = { .p = db_scale_wm_dac }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200798 }
799};
800
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100801static struct snd_kcontrol_new wm_controls[] __devinitdata = {
Simone Zinanniaed058e2005-04-11 14:08:40 +0200802 {
803 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
804 .name = "PCM Playback Switch",
805 .info = wm_pcm_mute_info,
806 .get = wm_pcm_mute_get,
807 .put = wm_pcm_mute_put
808 },
809 {
810 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwaif640c322006-08-30 16:57:37 +0200811 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
812 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
Simone Zinanniaed058e2005-04-11 14:08:40 +0200813 .name = "PCM Playback Volume",
814 .info = wm_pcm_vol_info,
815 .get = wm_pcm_vol_get,
Takashi Iwaif640c322006-08-30 16:57:37 +0200816 .put = wm_pcm_vol_put,
817 .tlv = { .p = db_scale_wm_pcm }
Simone Zinanniaed058e2005-04-11 14:08:40 +0200818 },
819 {
820 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
821 .name = "DAC Deemphasis Switch",
822 .info = phase28_deemp_info,
823 .get = phase28_deemp_get,
824 .put = phase28_deemp_put
825 },
826 {
827 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
828 .name = "ADC Oversampling",
829 .info = phase28_oversampling_info,
830 .get = phase28_oversampling_get,
831 .put = phase28_oversampling_put
832 }
833};
834
Takashi Iwaiab0c7d72005-11-17 15:00:18 +0100835static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
Simone Zinanniaed058e2005-04-11 14:08:40 +0200836{
837 unsigned int i, counts;
838 int err;
839
840 counts = ARRAY_SIZE(phase28_dac_controls);
841 for (i = 0; i < counts; i++) {
842 err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
843 if (err < 0)
844 return err;
845 }
846
847 for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
848 err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
849 if (err < 0)
850 return err;
851 }
852
853 return 0;
854}
855
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100856struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 {
858 .subvendor = VT1724_SUBDEVICE_PHASE22,
859 .name = "Terratec PHASE 22",
860 .model = "phase22",
861 .chip_init = phase22_init,
862 .build_controls = phase22_add_controls,
863 .eeprom_size = sizeof(phase22_eeprom),
864 .eeprom_data = phase22_eeprom,
865 },
Simone Zinanniaed058e2005-04-11 14:08:40 +0200866 {
867 .subvendor = VT1724_SUBDEVICE_PHASE28,
868 .name = "Terratec PHASE 28",
869 .model = "phase28",
870 .chip_init = phase28_init,
871 .build_controls = phase28_add_controls,
872 .eeprom_size = sizeof(phase28_eeprom),
873 .eeprom_data = phase28_eeprom,
874 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 { } /* terminator */
876};