Commit 0348756a authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA CVS update - Takashi Iwai <tiwai@suse.de>

PCI drivers,ICE1712 driver,ICE1724 driver
- improved the description of ice1724 driver on Kconfig.
- better support of VT1720 with snd-ice1724 driver.
- check PCI subsystem IDs when no EEPROM is available (ice1724 only)
- change the driver name string if given in the board list.
- merged prodigy 7.1 support into aureon.c.  they are almost identical.
- allow to use PDMA4 and RMDA1 for non-SPDIF purpose if specified (ice1724 only).
parent 15fe2ede
......@@ -270,15 +270,15 @@ config SND_ICE1712
TerraTec - EWX 24/96, EWS 88MT, EWS 88D, DMX 6Fire.
config SND_ICE1724
tristate "ICE/VT1724 (Envy24HT)"
tristate "ICE/VT1724/1720 (Envy24HT/PT)"
depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
help
Say 'Y' or 'M' to include support for ICE/VT1724 (Envy24HT) based
Say 'Y' or 'M' to include support for ICE/VT1724/1720 (Envy24HT/PT) based
soundcards.
Currently supported hardware is: MidiMan M Audio - Revolution 7.1,
AMP Ltd AUDIO2000.
AMP Ltd AUDIO2000, Terratec Aureon 5.1 Sky/7.1, AudioTrak Prodigy 7.1.
config SND_INTEL8X0
tristate "Intel i8x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111"
......
......@@ -5,7 +5,7 @@
snd-ice17xx-ak4xxx-objs := ak4xxx.o
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o prodigy.o
snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
......
......@@ -38,6 +38,25 @@
* the analog mixing but not easily controllable (it's not connected
* directly from envy24ht chip). so let's leave it as it is.
*
*
* Lowlevel functions for AudioTrak Prodigy 7.1 (and possibly 192) cards
* Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
*
* version 0.82: Stable / not all features work yet (no communication with AC97 secondary)
* added 64x/128x oversampling switch (should be 64x only for 96khz)
* fixed some recording labels (still need to check the rest)
* recording is working probably thanks to correct wm8770 initialization
*
* version 0.5: Initial release:
* working: analog output, mixer, headphone amplifier switch
* not working: prety much everything else, at least i could verify that
* we have no digital output, no capture, pretty bad clicks and poops
* on mixer switch and other coll stuff.
*
* - Prodigy boards are equipped with AC97 STAC9744 chip , too. it's used to do
* the analog mixing but not easily controllable (it's not connected
* directly from envy24ht chip). so let's leave it as it is.
*
*/
#include <sound/driver.h>
......@@ -77,12 +96,18 @@
static void aureon_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits)
{
unsigned int tmp;
unsigned int cscs;
int i;
tmp = snd_ice1712_gpio_read(ice);
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
cscs = PRODIGY_CS8415_CS;
else
cscs = AUREON_CS8415_CS;
snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RW|AUREON_WM_DATA|AUREON_WM_CLK|
AUREON_WM_CS|AUREON_CS8415_CS));
AUREON_WM_CS|cscs));
tmp |= AUREON_WM_RW;
tmp &= ~cs;
snd_ice1712_gpio_write(ice, tmp);
......@@ -395,7 +420,7 @@ static int aureon_get_headphone_amp(ice1712_t *ice)
return ( tmp & AUREON_HP_SEL )!= 0;
}
static int aureon_hpamp_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
static int aureon_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
......@@ -420,6 +445,76 @@ static int aureon_hpamp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
}
/*
* Deemphasis
*/
static int aureon_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
return 0;
}
static int aureon_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
int temp, temp2;
temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
if (ucontrol->value.integer.value[0])
temp |= 0xf;
else
temp &= ~0xf;
if (temp != temp2) {
wm_put(ice, WM_DAC_CTRL2, temp);
return 1;
}
return 0;
}
/*
* ADC Oversampling
*/
static int aureon_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
{
static char *texts[2] = { "128x", "64x" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int aureon_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
return 0;
}
static int aureon_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
int temp, temp2;
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
temp2 = temp = wm_get(ice, WM_MASTER);
if (ucontrol->value.enumerated.item[0])
temp |= 0x8;
else
temp &= ~0x8;
if (temp != temp2) {
wm_put(ice, WM_MASTER, temp);
return 1;
}
return 0;
}
/*
* mixers
*/
......@@ -485,10 +580,24 @@ static snd_kcontrol_new_t wm_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Amplifier Switch",
.info = aureon_hpamp_info,
.info = aureon_bool_info,
.get = aureon_hpamp_get,
.put = aureon_hpamp_put
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Deemphasis Switch",
.info = aureon_bool_info,
.get = aureon_deemp_get,
.put = aureon_deemp_put
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Oversampling",
.info = aureon_oversampling_info,
.get = aureon_oversampling_get,
.put = aureon_oversampling_put
},
};
......@@ -518,7 +627,7 @@ static int __devinit aureon_add_controls(ice1712_t *ice)
*/
static int __devinit aureon_init(ice1712_t *ice)
{
static unsigned short wm_inits[] = {
static unsigned short wm_inits_aureon[] = {
/* These come first to reduce init pop noise */
0x1b, 0x000, /* ADC Mux */
0x1c, 0x009, /* Out Mux1 */
......@@ -552,20 +661,66 @@ static int __devinit aureon_init(ice1712_t *ice)
0x15, 0x000, /* no deemphasis, no ZFLG */
0x19, 0x000, /* -12dB ADC/L */
0x1a, 0x000, /* -12dB ADC/R */
(unsigned short)-1
};
static unsigned short wm_inits_prodigy[] = {
/* These come first to reduce init pop noise */
0x1b, 0x000, /* ADC Mux */
0x1c, 0x009, /* Out Mux1 */
0x1d, 0x009, /* Out Mux2 */
0x18, 0x000, /* All power-up */
0x16, 0x022, /* I2S, normal polarity, 24bit, high-pass on */
0x17, 0x006, /* 128fs, slave mode */
0x00, 0, /* DAC1 analog mute */
0x01, 0, /* DAC2 analog mute */
0x02, 0, /* DAC3 analog mute */
0x03, 0, /* DAC4 analog mute */
0x04, 0, /* DAC5 analog mute */
0x05, 0, /* DAC6 analog mute */
0x06, 0, /* DAC7 analog mute */
0x07, 0, /* DAC8 analog mute */
0x08, 0x100, /* master analog mute */
0x09, 0x7f, /* DAC1 digital full */
0x0a, 0x7f, /* DAC2 digital full */
0x0b, 0x7f, /* DAC3 digital full */
0x0c, 0x7f, /* DAC4 digital full */
0x0d, 0x7f, /* DAC5 digital full */
0x0e, 0x7f, /* DAC6 digital full */
0x0f, 0x7f, /* DAC7 digital full */
0x10, 0x7f, /* DAC8 digital full */
0x11, 0x1FF, /* master digital full */
0x12, 0x000, /* phase normal */
0x13, 0x090, /* unmute DAC L/R */
0x14, 0x000, /* all unmute */
0x15, 0x000, /* no deemphasis, no ZFLG */
0x19, 0x000, /* -12dB ADC/L */
0x1a, 0x000, /* -12dB ADC/R */
(unsigned short)-1
};
static unsigned short cs_inits[] = {
0x0441, /* RUN */
0x0100, /* no mute */
0x0200, /* */
0x0600, /* slave, 24bit */
(unsigned short)-1
};
unsigned int tmp;
unsigned int i;
unsigned short *p;
unsigned int cscs;
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
ice->num_total_dacs = 6;
ice->num_total_adcs = 6;
} else {
/* aureon 7.1 and prodigy 7.1 */
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
}
......@@ -576,16 +731,22 @@ static int __devinit aureon_init(ice1712_t *ice)
return -ENOMEM;
ice->akm_codecs = 1;
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
cscs = PRODIGY_CS8415_CS;
else
cscs = AUREON_CS8415_CS;
snd_ice1712_gpio_set_dir(ice, 0xbfffff); /* fix this for the time being */
/* reset the wm codec as the SPI mode */
snd_ice1712_save_gpio_status(ice);
snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RESET|AUREON_WM_CS|AUREON_CS8415_CS));
snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RESET|AUREON_WM_CS|
cscs|AUREON_HP_SEL));
tmp = snd_ice1712_gpio_read(ice);
tmp &= ~AUREON_WM_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= AUREON_WM_CS | AUREON_CS8415_CS;
tmp |= AUREON_WM_CS | cscs;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= AUREON_WM_RESET;
......@@ -593,13 +754,19 @@ static int __devinit aureon_init(ice1712_t *ice)
udelay(1);
/* initialize WM8770 codec */
for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
wm_put(ice, wm_inits[i], wm_inits[i+1]);
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
p = wm_inits_prodigy;
else
p = wm_inits_aureon;
for (; *p != (unsigned short)-1; p += 2)
wm_put(ice, p[0], p[1]);
/* initialize CS8415A codec */
for (i = 0; i < ARRAY_SIZE(cs_inits); i++)
aureon_spi_write(ice, AUREON_CS8415_CS,
cs_inits[i] | 0x200000, 24);
for (p = cs_inits; *p != (unsigned short)-1; p++)
aureon_spi_write(ice, cscs,
*p | 0x200000, 24);
aureon_set_headphone_amp(ice, 1);
snd_ice1712_restore_gpio_status(ice);
......@@ -613,7 +780,7 @@ static int __devinit aureon_init(ice1712_t *ice)
*/
static unsigned char aureon51_eeprom[] __devinitdata = {
0x12, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 3DACs */
0x2a, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 3DACs */
0x80, /* ACLINK: I2S */
0xf8, /* I2S: vol, 96k, 24bit, 192k */
0xc3, /* SPDIF: out-en, out-int, spdif-in */
......@@ -629,7 +796,23 @@ static unsigned char aureon51_eeprom[] __devinitdata = {
};
static unsigned char aureon71_eeprom[] __devinitdata = {
0x13, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xf8, /* I2S: vol, 96k, 24bit, 192k */
0xc3, /* SPDIF: out-en, out-int, spdif-in */
0xff, /* GPIO_DIR */
0xff, /* GPIO_DIR1 */
0xbf, /* GPIO_DIR2 */
0x00, /* GPIO_MASK */
0x00, /* GPIO_MASK1 */
0x00, /* GPIO_MASK2 */
0x00, /* GPIO_STATE */
0x00, /* GPIO_STATE1 */
0x00, /* GPIO_STATE2 */
};
static unsigned char prodigy71_eeprom[] __devinitdata = {
0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xf8, /* I2S: vol, 96k, 24bit, 192k */
0xc3, /* SPDIF: out-en, out-int, spdif-in */
......@@ -667,10 +850,20 @@ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
.name = "Terratec Aureon 7.1-Universe",
/* model not needed - identical with 7.1-Space */
.chip_init = aureon_init,
.build_controls = aureon_add_controls,
.eeprom_size = sizeof(aureon71_eeprom),
.eeprom_data = aureon71_eeprom,
},
{
.subvendor = VT1724_SUBDEVICE_PRODIGY71,
.name = "Audiotrak Prodigy 7.1",
.model = "prodigy71",
.chip_init = aureon_init,
.build_controls = aureon_add_controls,
.eeprom_size = sizeof(prodigy71_eeprom),
.eeprom_data = prodigy71_eeprom,
},
{ } /* terminator */
};
......@@ -26,11 +26,13 @@
#define AUREON_DEVICE_DESC "{Terratec,Aureon 5.1 Sky},"\
"{Terratec,Aureon 7.1 Space},"\
"{Terratec,Aureon 7.1 Universe},"
"{Terratec,Aureon 7.1 Universe}," \
"{AudioTrak,Prodigy 7.1},"
#define VT1724_SUBDEVICE_AUREON51_SKY 0x3b154711 /* Aureon 5.1 Sky */
#define VT1724_SUBDEVICE_AUREON71_SPACE 0x3b154511 /* Aureon 7.1 Space */
#define VT1724_SUBDEVICE_AUREON71_UNIVERSE 0x3b155311 /* Aureon 7.1 Universe */
#define VT1724_SUBDEVICE_PRODIGY71 0x33495345 /* PRODIGY 7.1 */
extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[];
......@@ -46,4 +48,8 @@ extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[];
#define AUREON_HP_SEL (1 << 14)
#define AUREON_WM_CS (1 << 12)
/* Prodigy has different pin assignment for chip select */
#define PRODIGY_CS8415_CS (1 << 23)
#define PRODIGY_CS8415_CDTO (1 << 22)
#endif /* __SOUND_AUREON_H */
......@@ -2660,6 +2660,8 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
for (c = *tbl; c->subvendor; c++) {
if (c->subvendor == ice->eeprom.subvendor) {
strcpy(card->shortname, c->name);
if (c->driver) /* specific driver? */
strcpy(card->driver, c->driver);
if (c->chip_init) {
if ((err = c->chip_init(ice)) < 0) {
snd_card_free(card);
......
......@@ -329,6 +329,10 @@ struct _snd_ice1712 {
unsigned int pro_volumes[20];
unsigned int omni: 1; /* Delta Omni I/O */
unsigned int vt1724: 1;
unsigned int vt1720: 1;
unsigned int has_spdif: 1; /* VT1720/4 - has SPDIF I/O */
unsigned int force_pdma4: 1; /* VT1720/4 - PDMA4 as non-spdif */
unsigned int force_rdma1: 1; /* VT1720/4 - RDMA1 as non-spdif */
unsigned int num_total_dacs; /* total DACs */
unsigned int num_total_adcs; /* total ADCs */
unsigned char hoontech_boxbits[4];
......@@ -461,6 +465,7 @@ struct snd_ice1712_card_info {
unsigned int subvendor;
char *name;
char *model;
char *driver;
int (*chip_init)(ice1712_t *);
int (*build_controls)(ice1712_t *);
int no_mpu401: 1;
......
/*
* ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT)
* VIA VT1720 (Envy24PT)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
* 2002 James Stafford <jstafford@ampltd.com>
......@@ -43,21 +44,21 @@
#include "amp.h"
#include "revo.h"
#include "aureon.h"
#include "prodigy.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("ICEnsemble ICE1724 (Envy24HT)");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
MODULE_LICENSE("GPL");
MODULE_CLASSES("{sound}");
MODULE_DEVICES("{"
REVO_DEVICE_DESC
AMP_AUDIO2000_DEVICE_DESC
AUREON_DEVICE_DESC
PRODIGY_DEVICE_DESC
"{VIA,VT1720},"
"{VIA,VT1724},"
"{ICEnsemble,Generic ICE1724},"
"{ICEnsemble,Generic Envy24HT}}");
"{ICEnsemble,Generic Envy24HT}"
"{ICEnsemble,Generic Envy24PT}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
......@@ -84,6 +85,7 @@ MODULE_PARM_DESC(model, "Use the given board model.");
#define PCI_DEVICE_ID_VT1724 0x1724
#endif
/* Both VT1720 and VT1724 have the same PCI IDs */
static struct pci_device_id snd_vt1724_ids[] = {
{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
......@@ -232,6 +234,17 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id, struct pt_regs *r
status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);
}
if (status & VT1724_IRQ_MTPCM) {
/*
* Multi-track PCM
* PCM assignment are:
* Playback DMA0 (M/C) = playback_pro_substream
* Playback DMA1 = playback_con_substream_ds[0]
* Playback DMA2 = playback_con_substream_ds[1]
* Playback DMA3 = playback_con_substream_ds[2]
* Playback DMA4 (SPDIF) = playback_con_substream
* Record DMA0 = capture_pro_substream
* Record DMA1 = capture_con_substream
*/
unsigned char mtstat = inb(ICEMT1724(ice, IRQ));
if (mtstat & VT1724_MULTI_PDMA0) {
if (ice->playback_pro_substream)
......@@ -292,6 +305,12 @@ static snd_pcm_hw_constraint_list_t hw_constraints_rates_96 = {
.mask = 0,
};
static snd_pcm_hw_constraint_list_t hw_constraints_rates_48 = {
.count = ARRAY_SIZE(rates) - 5, /* up to 48000 */
.list = rates,
.mask = 0,
};
static snd_pcm_hw_constraint_list_t hw_constraints_rates_192 = {
.count = ARRAY_SIZE(rates),
.list = rates,
......@@ -427,6 +446,15 @@ static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force
if (ice->akm[i].ops.set_rate_val)
ice->akm[i].ops.set_rate_val(&ice->akm[i], rate);
}
/* set up AC97 registers if needed */
if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) && ice->ac97) {
snd_ac97_set_rate(ice->ac97, AC97_PCM_FRONT_DAC_RATE, rate);
snd_ac97_set_rate(ice->ac97, AC97_PCM_SURR_DAC_RATE, rate);
snd_ac97_set_rate(ice->ac97, AC97_PCM_LFE_DAC_RATE, rate);
snd_ac97_set_rate(ice->ac97, AC97_SPDIF, rate);
snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, rate);
}
}
static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream,
......@@ -439,6 +467,7 @@ static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream,
down(&ice->open_mutex);
/* mark surround channels */
if (substream == ice->playback_pro_substream) {
/* PDMA0 can be multi-channel up to 8 */
chs = chs / 2 - 1;
for (i = 0; i < chs; i++) {
if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
......@@ -453,6 +482,7 @@ static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream,
}
} else {
for (i = 0; i < 3; i++) {
/* check individual playback stream */
if (ice->playback_con_substream_ds[i] == substream) {
if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
up(&ice->open_mutex);
......@@ -613,7 +643,7 @@ static snd_pcm_hardware_t snd_vt1724_playback_pro =
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
.rate_min = 4000,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 2,
.channels_max = 8,
......@@ -624,6 +654,25 @@ static snd_pcm_hardware_t snd_vt1724_playback_pro =
.periods_max = 1024,
};
static snd_pcm_hardware_t snd_vt1724_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
.rate_min = 32000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = (1UL << 18), /* 16bits dword */
.period_bytes_min = 2 * 4 * 2,
.period_bytes_max = (1UL << 18),
.periods_min = 2,
.periods_max = 1024,
};
static snd_pcm_hardware_t snd_vt1724_2ch_stereo =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
......@@ -632,7 +681,7 @@ static snd_pcm_hardware_t snd_vt1724_2ch_stereo =
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
.rate_min = 4000,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 2,
.channels_max = 2,
......@@ -643,6 +692,41 @@ static snd_pcm_hardware_t snd_vt1724_2ch_stereo =
.periods_max = 1024,
};
/*
* set rate constraints
*/
static int set_rate_constraints(ice1712_t *ice, snd_pcm_substream_t *substream)
{
snd_pcm_runtime_t *runtime = substream->runtime;
if (ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) {
/* I2S */
if (ice->eeprom.data[ICE_EEP2_I2S] & 0x08)
return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
else {
runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000;
runtime->hw.rate_max = 96000;
return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
}
} else if (ice->ac97) {
/* ACLINK */
int ratec;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ratec = AC97_RATES_FRONT_DAC;
else
ratec = AC97_RATES_ADC;
runtime->hw.rates = ice->ac97->rates[ratec];
runtime->hw.rate_max = 48000;
if (runtime->hw.rates == SNDRV_PCM_RATE_48000) {
runtime->hw.rate_min = 48000;
return 0;
} else {
runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000;
return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_48);
}
}
return 0;
}
/* multi-channel playback needs alignment 8x32bit regarless of the channels
* actually used
*/
......@@ -659,12 +743,7 @@ static int snd_vt1724_playback_pro_open(snd_pcm_substream_t * substream)
runtime->hw = snd_vt1724_playback_pro;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) &&
(ice->eeprom.data[ICE_EEP2_I2S] & 0x08))
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
else
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
set_rate_constraints(ice, substream);
down(&ice->open_mutex);
/* calculate the currently available channels */
for (chs = 0; chs < 3; chs++) {
......@@ -693,11 +772,7 @@ static int snd_vt1724_capture_pro_open(snd_pcm_substream_t * substream)
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) &&
(ice->eeprom.data[ICE_EEP2_I2S] & 0x08))
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
else
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
set_rate_constraints(ice, substream);
return 0;
}
......@@ -816,11 +891,13 @@ static int snd_vt1724_playback_spdif_open(snd_pcm_substream_t *substream)
runtime->private_data = (void*)VT1724_PDMA4_START; /* irq/status/trigger bit */
ice->playback_con_substream = substream;
runtime->hw = snd_vt1724_2ch_stereo;
if (ice->force_pdma4) {
runtime->hw = snd_vt1724_2ch_stereo;
set_rate_constraints(ice, substream);
} else
runtime->hw = snd_vt1724_spdif;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
return 0;
}
......@@ -842,10 +919,13 @@ static int snd_vt1724_capture_spdif_open(snd_pcm_substream_t *substream)
runtime->private_data = (void*)VT1724_RDMA1_START; /* irq/status/trigger bit */
ice->capture_con_substream = substream;
runtime->hw = snd_vt1724_2ch_stereo;
if (ice->force_rdma1) {
runtime->hw = snd_vt1724_2ch_stereo;
set_rate_constraints(ice, substream);
} else
runtime->hw = snd_vt1724_spdif;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
return 0;
}
......@@ -885,22 +965,31 @@ static snd_pcm_ops_t snd_vt1724_capture_spdif_ops = {
static int __devinit snd_vt1724_pcm_spdif(ice1712_t * ice, int device)
{
char *name;
snd_pcm_t *pcm;
int play, capt;
int err;
if (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_OUT_INT)
if (ice->force_pdma4 ||
(ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_OUT_INT)) {
play = 1;
else
ice->has_spdif = 1;
} else
play = 0;
if (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN)
if (ice->force_rdma1 ||
(ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN)) {
capt = 1;
else
ice->has_spdif = 1;
} else
capt = 0;
if (! play && ! capt)
return 0; /* no spdif device */
err = snd_pcm_new(ice->card, "ICE1724 IEC958", device, play, capt, &pcm);
if (ice->force_pdma4 || ice->force_rdma1)
name = "ICE1724 Secondary";
else
name = "IEC1724 IEC958";
err = snd_pcm_new(ice->card, name, device, play, capt, &pcm);
if (err < 0)
return err;
......@@ -913,7 +1002,7 @@ static int __devinit snd_vt1724_pcm_spdif(ice1712_t * ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1724 IEC958");
strcpy(pcm->name, name);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
......@@ -987,8 +1076,7 @@ static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream)
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
set_rate_constraints(ice, substream);
return 0;
}
......@@ -1727,7 +1815,6 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_revo_cards,
snd_vt1724_amp_cards,
snd_vt1724_aureon_cards,
snd_vt1724_prodigy_cards,
0,
};
......@@ -1757,19 +1844,28 @@ void snd_vt1724_write_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr,
static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice, const char *modelname)
{
int dev = 0xa0; /* EEPROM device address */
const int dev = 0xa0; /* EEPROM device address */
unsigned int i, size;
struct snd_ice1712_card_info **tbl, *c;
if (! modelname || ! *modelname) {
if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) == 0) {
snd_printk(KERN_WARNING "ICE1724 has not detected EEPROM\n");
// return -EIO;
}
ice->eeprom.subvendor = (snd_vt1724_read_i2c(ice, dev, 0x00) << 0) |
ice->eeprom.subvendor = 0;
if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) != 0)
ice->eeprom.subvendor = (snd_vt1724_read_i2c(ice, dev, 0x00) << 0) |
(snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |
(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |
(snd_vt1724_read_i2c(ice, dev, 0x03) << 24);
if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
/* invalid subvendor from EEPROM, try the PCI subststem ID instead */
u16 vendor, device;
pci_read_config_word(ice->pci, PCI_SUBSYSTEM_VENDOR_ID, &vendor);
pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
ice->eeprom.subvendor = ((unsigned int)swab16(vendor) << 16) | swab16(device);
if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
printk(KERN_ERR "ice1724: No valid ID is found\n");
return -ENXIO;
}
}
}
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
......@@ -1779,7 +1875,7 @@ static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice, const char *modelnam
} else if (c->subvendor != ice->eeprom.subvendor)
continue;
if (! c->eeprom_size || ! c->eeprom_data)
break;
goto found;
/* if the EEPROM is given by the driver, use it */
snd_printdd("using the defined eeprom..\n");
ice->eeprom.version = 2;
......@@ -1788,18 +1884,19 @@ static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice, const char *modelnam
goto read_skipped;
}
}
printk(KERN_WARNING "ice1724: No matching model found for ID 0x%x\n", ice->eeprom.subvendor);
found:
ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04);
if (ice->eeprom.size < 6)
ice->eeprom.size = 32;
else if (ice->eeprom.size > 32) {
snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
printk(KERN_ERR "ice1724: Invalid EEPROM (size = %i)\n", ice->eeprom.size);
return -EIO;
}
ice->eeprom.version = snd_vt1724_read_i2c(ice, dev, 0x05);
if (ice->eeprom.version != 2) {
snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
// return -EIO;
}
if (ice->eeprom.version != 2)
printk(KERN_WARNING "ice1724: Invalid EEPROM version %i\n", ice->eeprom.version);
size = ice->eeprom.size - 6;
for (i = 0; i < size; i++)
ice->eeprom.data[i] = snd_vt1724_read_i2c(ice, dev, i + 6);
......@@ -1833,6 +1930,9 @@ static int __devinit snd_vt1724_chip_init(ice1712_t *ice)
outb(0, ICEREG1724(ice, POWERDOWN));
/* read back to check the availability of SPDIF out */
ice->eeprom.data[ICE_EEP2_SPDIF] = inb(ICEREG1724(ice, SPDIF_CFG));
return 0;
}
......@@ -1894,7 +1994,10 @@ static int __devinit snd_vt1724_build_controls(ice1712_t *ice)
if (ice->num_total_dacs > 0) {
snd_kcontrol_new_t tmp = snd_vt1724_mixer_pro_analog_route;
tmp.count = ice->num_total_dacs;
if (ice->vt1720)
tmp.count = 2;
else
tmp.count = ice->num_total_dacs;
err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
if (err < 0)
return err;
......@@ -2065,6 +2168,8 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
for (c = *tbl; c->subvendor; c++) {
if (c->subvendor == ice->eeprom.subvendor) {
strcpy(card->shortname, c->name);
if (c->driver) /* specific driver? */
strcpy(card->driver, c->driver);
if (c->chip_init) {
if ((err = c->chip_init(ice)) < 0) {
snd_card_free(card);
......@@ -2103,7 +2208,7 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
return err;
}
if (ice->pcm) { /* has SPDIF I/O */
if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) {
snd_card_free(card);
return err;
......
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for AudioTrak Prodigy 7.1 (and possibly 192) cards
* Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
* based on the aureon.c code (c) 2003 by Takashi Iwai <tiwai@suse.de>
*
* version 0.82: Stable / not all features work yet (no communication with AC97 secondary)
* added 64x/128x oversampling switch (should be 64x only for 96khz)
* fixed some recording labels (still need to check the rest)
* recording is working probably thanks to correct wm8770 initialization
*
* version 0.5: Initial release:
* working: analog output, mixer, headphone amplifier switch
* not working: prety much everything else, at least i could verify that
* we have no digital output, no capture, pretty bad clicks and poops
* on mixer switch and other coll stuff.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* NOTES:
*
*
*
* - we reuse the akm4xxx_t record for storing the wm8770 codec data.
* both wm and akm codecs are pretty similar, so we can integrate
* both controls in the future, once if wm codecs are reused in
* many boards.
*
* - writing over SPI is implemented but reading is not yet.
* the SPDIF-in channel status, etc. can be read from CS chip.
*
* - DAC digital volumes are not implemented in the mixer.
* if they show better response than DAC analog volumes, we can use them
* instead.
*
* - Prodigy boards are equipped with AC97 STAC9744 chip , too. it's used to do
* the analog mixing but not easily controllable (it's not connected
* directly from envy24ht chip). so let's leave it as it is.
*
*/
#define REVISION 0.82b
#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "prodigy.h"
static int prodigy_set_headphone_amp(ice1712_t *ice, int enable)
{
unsigned int tmp, tmp2;
tmp2 = tmp = snd_ice1712_gpio_read(ice);
if (enable)
tmp |= PRODIGY_HP_AMP_EN;
else
tmp &= ~ PRODIGY_HP_AMP_EN;
if (tmp != tmp2) {
snd_ice1712_gpio_write(ice, tmp);
return 1;
}
return 0;
}
static int prodigy_get_headphone_amp(ice1712_t *ice)
{
unsigned int tmp = snd_ice1712_gpio_read(ice);
return ( tmp & PRODIGY_HP_AMP_EN )!= 0;
}
/*
* write data in the SPI mode
*/
static void prodigy_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits)
{
unsigned int tmp;
int i;
tmp = snd_ice1712_gpio_read(ice);
snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_WM_RW|PRODIGY_WM_DATA|PRODIGY_WM_CLK|
PRODIGY_WM_CS|PRODIGY_CS8415_CS|PRODIGY_HP_AMP_EN));
tmp |= PRODIGY_WM_RW;
tmp &= ~cs;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
for (i = bits - 1; i >= 0; i--) {
tmp &= ~PRODIGY_WM_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
if (data & (1 << i))
tmp |= PRODIGY_WM_DATA;
else
tmp &= ~PRODIGY_WM_DATA;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PRODIGY_WM_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
tmp &= ~PRODIGY_WM_CLK;
tmp |= cs;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PRODIGY_WM_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
/*
* get the current register value of WM codec
*/
static unsigned short wm_get(ice1712_t *ice, int reg)
{
reg <<= 1;
return ((unsigned short)ice->akm[0].images[reg] << 8) |
ice->akm[0].images[reg + 1];
}
/*
* set the register value of WM codec and remember it
*/
static void wm_put(ice1712_t *ice, int reg, unsigned short val)
{
prodigy_spi_write(ice, PRODIGY_WM_CS, (reg << 9) | (val & 0x1ff), 16);
reg <<= 1;
ice->akm[0].images[reg] = val >> 8;
ice->akm[0].images[reg + 1] = val;
}
/*********************************
********* Controls section ******
*********************************/
#define PRODIGY_CON_HPAMP \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = "Headphone Amplifier", \
.info = prodigy_hpamp_info, \
.get = prodigy_hpamp_get, \
.put = prodigy_hpamp_put \
}
static int prodigy_hpamp_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
{
static char *texts[2] = {
"Off", "On"
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int prodigy_hpamp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = prodigy_get_headphone_amp(ice);
return 0;
}
static int prodigy_hpamp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
return prodigy_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
}
#define PRODIGY_CON_DEEMP \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = "DAC De-emphasis", \
.info = prodigy_deemp_info, \
.get = prodigy_deemp_get, \
.put = prodigy_deemp_put \
}
static int prodigy_deemp_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
{
static char *texts[2] = { "Off", "On" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int prodigy_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = (wm_get(ice, 0x15) & 0xf) == 0xf;
return 0;
}
static int prodigy_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
int temp, temp2;
temp2 = temp = wm_get(ice, 0x15);
temp = (temp & ~0xf) | ((ucontrol->value.integer.value[0])*0xf);
if (temp != temp2) {
wm_put(ice,0x15,temp);
return 1;
}
return 0;
}
#define PRODIGY_CON_OVERSAMPLING \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = "ADC Oversampling", \
.info = prodigy_oversampling_info, \
.get = prodigy_oversampling_get, \
.put = prodigy_oversampling_put \
}
static int prodigy_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
{
static char *texts[2] = { "128x", "64x" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int prodigy_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = (wm_get(ice, 0x17) & 0x8) == 0x8;
return 0;
}
static int prodigy_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
int temp, temp2;
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
temp2 = temp = wm_get(ice, 0x17);
if( ucontrol->value.integer.value[0] ) {
temp |= 0x8;
} else {
temp &= ~0x8;
}
if (temp != temp2) {
wm_put(ice,0x17,temp);
return 1;
}
return 0;
}
/*
* DAC volume attenuation mixer control
*/
static int wm_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0; /* mute */
uinfo->value.integer.max = 101; /* 0dB */
return 0;
}
static int wm_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
int idx;
unsigned short vol;
down(&ice->gpio_mutex);
if (kcontrol->private_value)
idx = WM_DAC_MASTER_ATTEN;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_DAC_ATTEN;
vol = wm_get(ice, idx) & 0x7f;
if (vol <= 0x1a)
ucontrol->value.integer.value[0] = 0;
else
ucontrol->value.integer.value[0] = vol - 0x1a;
up(&ice->gpio_mutex);
return 0;
}
static int wm_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
int idx;
unsigned short ovol, nvol;
int change;
snd_ice1712_save_gpio_status(ice);
if (kcontrol->private_value)
idx = WM_DAC_MASTER_ATTEN;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_DAC_ATTEN;
nvol = ucontrol->value.integer.value[0] + 0x1a;
ovol = wm_get(ice, idx) & 0x7f;
change = (ovol != nvol);
if (change) {
if (nvol <= 0x1a && ovol <= 0x1a)
change = 0;
else
wm_put(ice, idx, nvol | 0x180); /* update on zero detect */
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
/*
* ADC gain mixer control
*/
static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0; /* -12dB */
uinfo->value.integer.max = 0x1f; /* 19dB */
return 0;
}
static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
int idx;
unsigned short vol;
down(&ice->gpio_mutex);
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_ADC_GAIN;
vol = wm_get(ice, idx) & 0x1f;
ucontrol->value.integer.value[0] = vol;
up(&ice->gpio_mutex);
return 0;
}
static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
int idx;
unsigned short ovol, nvol;
int change;
snd_ice1712_save_gpio_status(ice);
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_ADC_GAIN;
nvol = ucontrol->value.integer.value[0];
ovol = wm_get(ice, idx) & 0x1f;
change = (ovol != nvol);
if (change)
wm_put(ice, idx, nvol);
snd_ice1712_restore_gpio_status(ice);
return change;
}
/*
* ADC input mux mixer control
*/
static int wm_adc_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
static char *texts[] = {
"CD Left",
"CD Right",
"Line Left",
"Line Right",
"Aux Left",
"Aux Right",
"Mic Left",
"Mic Right",
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 2;
uinfo->value.enumerated.items = 8;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int wm_adc_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
down(&ice->gpio_mutex);
val = wm_get(ice, WM_ADC_MUX);
ucontrol->value.integer.value[0] = val & 7;
ucontrol->value.integer.value[1] = (val >> 4) & 7;
up(&ice->gpio_mutex);
return 0;
}
static int wm_adc_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ice1712_t *ice = snd_kcontrol_chip(kcontrol);
unsigned short oval, nval;
int change;
snd_ice1712_save_gpio_status(ice);
oval = wm_get(ice, WM_ADC_MUX);
nval = oval & ~0x77;
nval |= ucontrol->value.integer.value[0] & 7;
nval |= (ucontrol->value.integer.value[1] & 7) << 4;
change = (oval != nval);
if (change)
wm_put(ice, WM_ADC_MUX, nval);
snd_ice1712_restore_gpio_status(ice);
return 0;
}
/*
* mixers
*/
static snd_kcontrol_new_t prodigy71_dac_control __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Volume",
.count = 8,
.info = wm_dac_vol_info,
.get = wm_dac_vol_get,
.put = wm_dac_vol_put,
};
static snd_kcontrol_new_t wm_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.info = wm_dac_vol_info,
.get = wm_dac_vol_get,
.put = wm_dac_vol_put,
.private_value = 1,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Volume",
.count = 2,
.info = wm_adc_vol_info,
.get = wm_adc_vol_get,
.put = wm_adc_vol_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Route",
.info = wm_adc_mux_info,
.get = wm_adc_mux_get,
.put = wm_adc_mux_put,
},
PRODIGY_CON_HPAMP ,
PRODIGY_CON_DEEMP ,
PRODIGY_CON_OVERSAMPLING
};
static int __devinit prodigy_add_controls(ice1712_t *ice)
{
unsigned int i;
int err;
err = snd_ctl_add(ice->card, snd_ctl_new1(&prodigy71_dac_control, ice));
if (err < 0)
return err;
for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
if (err < 0)
return err;
}
return 0;
}
/*
* initialize the chip
*/
static int __devinit prodigy_init(ice1712_t *ice)
{
static unsigned short wm_inits[] = {
/* These come first to reduce init pop noise */
0x1b, 0x000, /* ADC Mux */
0x1c, 0x009, /* Out Mux1 */
0x1d, 0x009, /* Out Mux2 */
0x18, 0x000, /* All power-up */
0x16, 0x022, /* I2S, normal polarity, 24bit, high-pass on */
0x17, 0x006, /* 128fs, slave mode */
0x00, 0, /* DAC1 analog mute */
0x01, 0, /* DAC2 analog mute */
0x02, 0, /* DAC3 analog mute */
0x03, 0, /* DAC4 analog mute */
0x04, 0, /* DAC5 analog mute */
0x05, 0, /* DAC6 analog mute */
0x06, 0, /* DAC7 analog mute */
0x07, 0, /* DAC8 analog mute */
0x08, 0x100, /* master analog mute */
0x09, 0x7f, /* DAC1 digital full */
0x0a, 0x7f, /* DAC2 digital full */
0x0b, 0x7f, /* DAC3 digital full */
0x0c, 0x7f, /* DAC4 digital full */
0x0d, 0x7f, /* DAC5 digital full */
0x0e, 0x7f, /* DAC6 digital full */
0x0f, 0x7f, /* DAC7 digital full */
0x10, 0x7f, /* DAC8 digital full */
0x11, 0x1FF, /* master digital full */
0x12, 0x000, /* phase normal */
0x13, 0x090, /* unmute DAC L/R */
0x14, 0x000, /* all unmute */
0x15, 0x000, /* no deemphasis, no ZFLG */
0x19, 0x000, /* -12dB ADC/L */
0x1a, 0x000 /* -12dB ADC/R */
};
static unsigned short cs_inits[] = {
0x0441, /* RUN */
0x0100, /* no mute */
0x0200, /* */
0x0600, /* slave, 24bit */
};
unsigned int tmp;
unsigned int i;
printk(KERN_INFO "ice1724: AudioTrak Prodigy 7.1 driver rev. 0.82b\n");
printk(KERN_INFO "ice1724: This driver is in beta stage. Forsuccess/failure reporting contact\n");
printk(KERN_INFO "ice1724: Apostolos Dimitromanolakis <apostol@cs.utoronto.ca>\n");
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
/* to remeber the register values */
ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL);
if (! ice->akm)
return -ENOMEM;
ice->akm_codecs = 1;
snd_ice1712_gpio_set_dir(ice, 0xbfffff); /* fix this for the time being */
/* reset the wm codec as the SPI mode */
snd_ice1712_save_gpio_status(ice);
snd_ice1712_gpio_set_mask(ice,~( PRODIGY_WM_RESET|PRODIGY_WM_CS|
PRODIGY_CS8415_CS|PRODIGY_HP_AMP_EN ));
tmp = snd_ice1712_gpio_read(ice);
tmp &= ~PRODIGY_WM_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PRODIGY_WM_CS | PRODIGY_CS8415_CS;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PRODIGY_WM_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
/* initialize WM8770 codec */
for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
wm_put(ice, wm_inits[i], wm_inits[i+1]);
/* initialize CS8415A codec */
for (i = 0; i < ARRAY_SIZE(cs_inits); i++)
prodigy_spi_write(ice, PRODIGY_CS8415_CS,
cs_inits[i] | 0x200000, 24);
prodigy_set_headphone_amp(ice, 1);
snd_ice1712_restore_gpio_status(ice);
return 0;
}
/*
* Prodigy boards don't provide the EEPROM data except for the vendor IDs.
* hence the driver needs to sets up it properly.
*/
static unsigned char prodigy71_eeprom[] __devinitdata = {
0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xf8, /* I2S: vol, 96k, 24bit, 192k */
0xc3, /* SPDIF: out-en, out-int, spdif-in */
0xff, /* GPIO_DIR */
0xff, /* GPIO_DIR1 */
0xbf, /* GPIO_DIR2 */
0x00, /* GPIO_MASK */
0x00, /* GPIO_MASK1 */
0x00, /* GPIO_MASK2 */
0x00, /* GPIO_STATE */
0x00, /* GPIO_STATE1 */
0x00, /* GPIO_STATE2 */
};
/* entry point */
struct snd_ice1712_card_info snd_vt1724_prodigy_cards[] __devinitdata = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY71,
.name = "Audiotrak Prodigy 7.1",
.model = "prodigy71",
.chip_init = prodigy_init,
.build_controls = prodigy_add_controls,
.eeprom_size = sizeof(prodigy71_eeprom),
.eeprom_data = prodigy71_eeprom,
},
{ } /* terminator */
};
#ifndef __SOUND_PRODIGY_H
#define __SOUND_PRODIGY_H
/*
* ALSA driver for VIA VT1724 (Envy24HT)
*
* Lowlevel functions for Terratec PRODIGY cards
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define PRODIGY_DEVICE_DESC "{AudioTrak,Prodigy 7.1},"
#define VT1724_SUBDEVICE_PRODIGY71 0x33495345 /* PRODIGY 7.1 */
extern struct snd_ice1712_card_info snd_vt1724_prodigy_cards[];
/* GPIO bits */
#define PRODIGY_CS8415_CS (1 << 23)
#define PRODIGY_CS8415_CDTO (1 << 22)
#define PRODIGY_WM_RESET (1 << 20)
#define PRODIGY_WM_CLK (1 << 19)
#define PRODIGY_WM_DATA (1 << 18)
#define PRODIGY_WM_RW (1 << 17)
#define PRODIGY_AC97_RESET (1 << 16)
#define PRODIGY_DIGITAL_SEL1 (1 << 15)
// #define PRODIGY_HP_SEL (1 << 14)
#define PRODIGY_WM_CS (1 << 12)
#define PRODIGY_HP_AMP_EN (1 << 14)
/* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
#define WM_DAC_DIG_MATER_ATTEN 0x11 /* DAC master digital attenuation */
#define WM_PHASE_SWAP 0x12 /* DAC phase */
#define WM_DAC_CTRL1 0x13 /* DAC control bits */
#define WM_MUTE 0x14 /* mute controls */
#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
#define WM_INT_CTRL 0x16 /* interface control */
#define WM_MASTER 0x17 /* master clock and mode */
#define WM_POWERDOWN 0x18 /* power-down controls */
#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
#define WM_ADC_MUX 0x1b /* input MUX */
#define WM_OUT_MUX1 0x1c /* output MUX */
#define WM_OUT_MUX2 0x1e /* output MUX */
#define WM_RESET 0x1f /* software reset */
#endif /* __SOUND_PRODIGY_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment