2 * ALSA driver for ICEnsemble VT1724 (Envy24HT)
4 * Lowlevel functions for AudioTrak Prodigy 7.1 (and possibly 192) cards
5 * Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
6 * based on the aureon.c code (c) 2003 by Takashi Iwai <tiwai@suse.de>
8 * version 0.82: Stable / not all features work yet (no communication with AC97 secondary)
9 * added 64x/128x oversampling switch (should be 64x only for 96khz)
10 * fixed some recording labels (still need to check the rest)
11 * recording is working probably thanks to correct wm8770 initialization
13 * version 0.5: Initial release:
14 * working: analog output, mixer, headphone amplifier switch
15 * not working: prety much everything else, at least i could verify that
16 * we have no digital output, no capture, pretty bad clicks and poops
17 * on mixer switch and other coll stuff.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
38 * - we reuse the akm4xxx_t record for storing the wm8770 codec data.
39 * both wm and akm codecs are pretty similar, so we can integrate
40 * both controls in the future, once if wm codecs are reused in
43 * - writing over SPI is implemented but reading is not yet.
44 * the SPDIF-in channel status, etc. can be read from CS chip.
46 * - DAC digital volumes are not implemented in the mixer.
47 * if they show better response than DAC analog volumes, we can use them
50 * - Prodigy boards are equipped with AC97 STAC9744 chip , too. it's used to do
51 * the analog mixing but not easily controllable (it's not connected
52 * directly from envy24ht chip). so let's leave it as it is.
56 #define REVISION 0.82b
58 #include <sound/driver.h>
60 #include <linux/delay.h>
61 #include <linux/interrupt.h>
62 #include <linux/init.h>
63 #include <linux/slab.h>
64 #include <sound/core.h>
71 static int prodigy_set_headphone_amp(ice1712_t *ice, int enable)
73 unsigned int tmp, tmp2;
75 tmp2 = tmp = snd_ice1712_gpio_read(ice);
77 tmp |= PRODIGY_HP_AMP_EN;
79 tmp &= ~ PRODIGY_HP_AMP_EN;
81 snd_ice1712_gpio_write(ice, tmp);
88 static int prodigy_get_headphone_amp(ice1712_t *ice)
90 unsigned int tmp = snd_ice1712_gpio_read(ice);
92 return ( tmp & PRODIGY_HP_AMP_EN )!= 0;
97 * write data in the SPI mode
99 static void prodigy_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits)
104 tmp = snd_ice1712_gpio_read(ice);
106 snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_WM_RW|PRODIGY_WM_DATA|PRODIGY_WM_CLK|
107 PRODIGY_WM_CS|PRODIGY_CS8415_CS|PRODIGY_HP_AMP_EN));
108 tmp |= PRODIGY_WM_RW;
110 snd_ice1712_gpio_write(ice, tmp);
113 for (i = bits - 1; i >= 0; i--) {
114 tmp &= ~PRODIGY_WM_CLK;
115 snd_ice1712_gpio_write(ice, tmp);
118 tmp |= PRODIGY_WM_DATA;
120 tmp &= ~PRODIGY_WM_DATA;
121 snd_ice1712_gpio_write(ice, tmp);
123 tmp |= PRODIGY_WM_CLK;
124 snd_ice1712_gpio_write(ice, tmp);
128 tmp &= ~PRODIGY_WM_CLK;
130 snd_ice1712_gpio_write(ice, tmp);
132 tmp |= PRODIGY_WM_CLK;
133 snd_ice1712_gpio_write(ice, tmp);
139 * get the current register value of WM codec
141 static unsigned short wm_get(ice1712_t *ice, int reg)
144 return ((unsigned short)ice->akm[0].images[reg] << 8) |
145 ice->akm[0].images[reg + 1];
149 * set the register value of WM codec and remember it
151 static void wm_put(ice1712_t *ice, int reg, unsigned short val)
153 prodigy_spi_write(ice, PRODIGY_WM_CS, (reg << 9) | (val & 0x1ff), 16);
155 ice->akm[0].images[reg] = val >> 8;
156 ice->akm[0].images[reg + 1] = val;
160 /*********************************
161 ********* Controls section ******
162 *********************************/
164 #define PRODIGY_CON_HPAMP \
166 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
167 .name = "Headphone Amplifier", \
168 .info = prodigy_hpamp_info, \
169 .get = prodigy_hpamp_get, \
170 .put = prodigy_hpamp_put \
173 static int prodigy_hpamp_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
175 static char *texts[2] = {
179 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
181 uinfo->value.enumerated.items = 2;
183 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
184 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
185 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
191 static int prodigy_hpamp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
193 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
195 ucontrol->value.integer.value[0] = prodigy_get_headphone_amp(ice);
200 static int prodigy_hpamp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
202 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
204 return prodigy_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
209 #define PRODIGY_CON_DEEMP \
211 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
212 .name = "DAC De-emphasis", \
213 .info = prodigy_deemp_info, \
214 .get = prodigy_deemp_get, \
215 .put = prodigy_deemp_put \
218 static int prodigy_deemp_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
220 static char *texts[2] = { "Off", "On" };
222 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
224 uinfo->value.enumerated.items = 2;
226 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
227 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
228 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
233 static int prodigy_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
235 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
236 ucontrol->value.integer.value[0] = (wm_get(ice, 0x15) & 0xf) == 0xf;
240 static int prodigy_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
242 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
244 temp2 = temp = wm_get(ice, 0x15);
245 temp = (temp & ~0xf) | ((ucontrol->value.integer.value[0])*0xf);
247 wm_put(ice,0x15,temp);
254 #define PRODIGY_CON_OVERSAMPLING \
256 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
257 .name = "ADC Oversampling", \
258 .info = prodigy_oversampling_info, \
259 .get = prodigy_oversampling_get, \
260 .put = prodigy_oversampling_put \
263 static int prodigy_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
265 static char *texts[2] = { "128x", "64x" };
267 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
269 uinfo->value.enumerated.items = 2;
271 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
272 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
273 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
278 static int prodigy_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
280 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
281 ucontrol->value.integer.value[0] = (wm_get(ice, 0x17) & 0x8) == 0x8;
285 static int prodigy_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
288 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
290 temp2 = temp = wm_get(ice, 0x17);
292 if( ucontrol->value.integer.value[0] ) {
299 wm_put(ice,0x17,temp);
309 * DAC volume attenuation mixer control
311 static int wm_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
313 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
315 uinfo->value.integer.min = 0; /* mute */
316 uinfo->value.integer.max = 101; /* 0dB */
320 static int wm_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
322 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
326 down(&ice->gpio_mutex);
327 if (kcontrol->private_value)
328 idx = WM_DAC_MASTER_ATTEN;
330 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_DAC_ATTEN;
331 vol = wm_get(ice, idx) & 0x7f;
333 ucontrol->value.integer.value[0] = 0;
335 ucontrol->value.integer.value[0] = vol - 0x1a;
336 up(&ice->gpio_mutex);
341 static int wm_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
343 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
345 unsigned short ovol, nvol;
348 snd_ice1712_save_gpio_status(ice);
349 if (kcontrol->private_value)
350 idx = WM_DAC_MASTER_ATTEN;
352 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_DAC_ATTEN;
353 nvol = ucontrol->value.integer.value[0] + 0x1a;
354 ovol = wm_get(ice, idx) & 0x7f;
355 change = (ovol != nvol);
357 if (nvol <= 0x1a && ovol <= 0x1a)
360 wm_put(ice, idx, nvol | 0x180); /* update on zero detect */
362 snd_ice1712_restore_gpio_status(ice);
367 * ADC gain mixer control
369 static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
371 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
373 uinfo->value.integer.min = 0; /* -12dB */
374 uinfo->value.integer.max = 0x1f; /* 19dB */
378 static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
380 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
384 down(&ice->gpio_mutex);
385 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_ADC_GAIN;
386 vol = wm_get(ice, idx) & 0x1f;
387 ucontrol->value.integer.value[0] = vol;
388 up(&ice->gpio_mutex);
392 static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
394 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
396 unsigned short ovol, nvol;
399 snd_ice1712_save_gpio_status(ice);
400 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_ADC_GAIN;
401 nvol = ucontrol->value.integer.value[0];
402 ovol = wm_get(ice, idx) & 0x1f;
403 change = (ovol != nvol);
405 wm_put(ice, idx, nvol);
406 snd_ice1712_restore_gpio_status(ice);
411 * ADC input mux mixer control
413 static int wm_adc_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
415 static char *texts[] = {
425 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
427 uinfo->value.enumerated.items = 8;
428 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
429 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
430 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
434 static int wm_adc_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
436 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
439 down(&ice->gpio_mutex);
440 val = wm_get(ice, WM_ADC_MUX);
441 ucontrol->value.integer.value[0] = val & 7;
442 ucontrol->value.integer.value[1] = (val >> 4) & 7;
443 up(&ice->gpio_mutex);
447 static int wm_adc_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
449 ice1712_t *ice = snd_kcontrol_chip(kcontrol);
450 unsigned short oval, nval;
453 snd_ice1712_save_gpio_status(ice);
454 oval = wm_get(ice, WM_ADC_MUX);
456 nval |= ucontrol->value.integer.value[0] & 7;
457 nval |= (ucontrol->value.integer.value[1] & 7) << 4;
458 change = (oval != nval);
460 wm_put(ice, WM_ADC_MUX, nval);
461 snd_ice1712_restore_gpio_status(ice);
469 static snd_kcontrol_new_t prodigy71_dac_control __devinitdata = {
470 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
471 .name = "DAC Volume",
473 .info = wm_dac_vol_info,
474 .get = wm_dac_vol_get,
475 .put = wm_dac_vol_put,
478 static snd_kcontrol_new_t wm_controls[] __devinitdata = {
480 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
481 .name = "Master Playback Volume",
482 .info = wm_dac_vol_info,
483 .get = wm_dac_vol_get,
484 .put = wm_dac_vol_put,
488 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
489 .name = "ADC Volume",
491 .info = wm_adc_vol_info,
492 .get = wm_adc_vol_get,
493 .put = wm_adc_vol_put,
496 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
497 .name = "Capture Route",
498 .info = wm_adc_mux_info,
499 .get = wm_adc_mux_get,
500 .put = wm_adc_mux_put,
504 PRODIGY_CON_OVERSAMPLING
508 static int __devinit prodigy_add_controls(ice1712_t *ice)
513 err = snd_ctl_add(ice->card, snd_ctl_new1(&prodigy71_dac_control, ice));
517 for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
518 err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
527 * initialize the chip
529 static int __devinit prodigy_init(ice1712_t *ice)
531 static unsigned short wm_inits[] = {
533 /* These come first to reduce init pop noise */
534 0x1b, 0x000, /* ADC Mux */
535 0x1c, 0x009, /* Out Mux1 */
536 0x1d, 0x009, /* Out Mux2 */
538 0x18, 0x000, /* All power-up */
540 0x16, 0x022, /* I2S, normal polarity, 24bit, high-pass on */
541 0x17, 0x006, /* 128fs, slave mode */
543 0x00, 0, /* DAC1 analog mute */
544 0x01, 0, /* DAC2 analog mute */
545 0x02, 0, /* DAC3 analog mute */
546 0x03, 0, /* DAC4 analog mute */
547 0x04, 0, /* DAC5 analog mute */
548 0x05, 0, /* DAC6 analog mute */
549 0x06, 0, /* DAC7 analog mute */
550 0x07, 0, /* DAC8 analog mute */
551 0x08, 0x100, /* master analog mute */
553 0x09, 0x7f, /* DAC1 digital full */
554 0x0a, 0x7f, /* DAC2 digital full */
555 0x0b, 0x7f, /* DAC3 digital full */
556 0x0c, 0x7f, /* DAC4 digital full */
557 0x0d, 0x7f, /* DAC5 digital full */
558 0x0e, 0x7f, /* DAC6 digital full */
559 0x0f, 0x7f, /* DAC7 digital full */
560 0x10, 0x7f, /* DAC8 digital full */
561 0x11, 0x1FF, /* master digital full */
563 0x12, 0x000, /* phase normal */
564 0x13, 0x090, /* unmute DAC L/R */
565 0x14, 0x000, /* all unmute */
566 0x15, 0x000, /* no deemphasis, no ZFLG */
568 0x19, 0x000, /* -12dB ADC/L */
569 0x1a, 0x000 /* -12dB ADC/R */
573 static unsigned short cs_inits[] = {
575 0x0100, /* no mute */
577 0x0600, /* slave, 24bit */
583 printk(KERN_INFO "ice1724: AudioTrak Prodigy 7.1 driver rev. 0.82b\n");
584 printk(KERN_INFO "ice1724: This driver is in beta stage. Forsuccess/failure reporting contact\n");
585 printk(KERN_INFO "ice1724: Apostolos Dimitromanolakis <apostol@cs.utoronto.ca>\n");
587 ice->num_total_dacs = 8;
588 ice->num_total_adcs = 8;
590 /* to remeber the register values */
591 ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL);
596 snd_ice1712_gpio_set_dir(ice, 0xbfffff); /* fix this for the time being */
598 /* reset the wm codec as the SPI mode */
599 snd_ice1712_save_gpio_status(ice);
600 snd_ice1712_gpio_set_mask(ice,~( PRODIGY_WM_RESET|PRODIGY_WM_CS|
601 PRODIGY_CS8415_CS|PRODIGY_HP_AMP_EN ));
603 tmp = snd_ice1712_gpio_read(ice);
604 tmp &= ~PRODIGY_WM_RESET;
605 snd_ice1712_gpio_write(ice, tmp);
607 tmp |= PRODIGY_WM_CS | PRODIGY_CS8415_CS;
608 snd_ice1712_gpio_write(ice, tmp);
610 tmp |= PRODIGY_WM_RESET;
611 snd_ice1712_gpio_write(ice, tmp);
614 /* initialize WM8770 codec */
615 for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
616 wm_put(ice, wm_inits[i], wm_inits[i+1]);
618 /* initialize CS8415A codec */
619 for (i = 0; i < ARRAY_SIZE(cs_inits); i++)
620 prodigy_spi_write(ice, PRODIGY_CS8415_CS,
621 cs_inits[i] | 0x200000, 24);
624 prodigy_set_headphone_amp(ice, 1);
626 snd_ice1712_restore_gpio_status(ice);
632 * Prodigy boards don't provide the EEPROM data except for the vendor IDs.
633 * hence the driver needs to sets up it properly.
636 static unsigned char prodigy71_eeprom[] __devinitdata = {
637 0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
638 0x80, /* ACLINK: I2S */
639 0xf8, /* I2S: vol, 96k, 24bit, 192k */
640 0xc3, /* SPDIF: out-en, out-int, spdif-in */
642 0xff, /* GPIO_DIR1 */
643 0xbf, /* GPIO_DIR2 */
644 0x00, /* GPIO_MASK */
645 0x00, /* GPIO_MASK1 */
646 0x00, /* GPIO_MASK2 */
647 0x00, /* GPIO_STATE */
648 0x00, /* GPIO_STATE1 */
649 0x00, /* GPIO_STATE2 */
653 struct snd_ice1712_card_info snd_vt1724_prodigy_cards[] __devinitdata = {
655 .subvendor = VT1724_SUBDEVICE_PRODIGY71,
656 .name = "Audiotrak Prodigy 7.1",
657 .chip_init = prodigy_init,
658 .build_controls = prodigy_add_controls,
659 .eeprom_size = sizeof(prodigy71_eeprom),
660 .eeprom_data = prodigy71_eeprom,