[ALSA] Add Aux input switch control for Aureon Universe
This patch adds a mixer control which allows the user to switch the Aux playback between the internal Aux jack, Wavetable, and Rear Line-In on Aureon Universe cards. For switching, a PCA9554 (8-line GPIO with I2C interface) and a 74HC4052 (dual 4-way mux/demux) are used. Output 0 and 1 of the PCA9554 are connected to the select pins of the 74HC4052. The I2C interface of the PCA9554 is connected to the card's internal SPI bus which is also used to control the WM8770 and CS8415. SPI and I2C on the same lines... To communicate with the PCA9554 the WM8770 and CS8415 are disabled and an I2C Stop Condition is generated before the Start Condition (needed for synchronisation because other SPI traffic appear to confuse the PCA9554). Then a normal I2C data transfer takes place. Programming must be done ridiculously slow; in theory, 4.7us is the minimum delay time for normal-speed I2C according to the datasheet, but even with 10us switching was unreliable. The Windows driver from Terratec does the programming very slowly, too (checked with an oscilloscope). PCA9554 datasheet: http://www.semiconductors.philips.com/acrobat/datasheets/PCA9554_9554A_6.pdf 74HC4052 datasheet: http://www.semiconductors.philips.com/acrobat/datasheets/74HC_HCT4052_4.pdf Signed-off-by: Maximilian Rehkopf <otakon@gmx.net> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
0bd43b5bc9
commit
af9b70ac00
|
@ -87,7 +87,151 @@
|
|||
#define CS8415_C_BUFFER 0x20
|
||||
#define CS8415_ID 0x7F
|
||||
|
||||
static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, unsigned short val) {
|
||||
/* PCA9554 registers */
|
||||
#define PCA9554_DEV 0x40 /* I2C device address */
|
||||
#define PCA9554_IN 0x00 /* input port */
|
||||
#define PCA9554_OUT 0x01 /* output port */
|
||||
#define PCA9554_INVERT 0x02 /* input invert */
|
||||
#define PCA9554_DIR 0x03 /* port directions */
|
||||
|
||||
/*
|
||||
* Aureon Universe additional controls using PCA9554
|
||||
*/
|
||||
|
||||
/*
|
||||
* Send data to pca9554
|
||||
*/
|
||||
static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
|
||||
unsigned char data)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int i, j;
|
||||
unsigned char dev = PCA9554_DEV; /* ID 0100000, write */
|
||||
unsigned char val = 0;
|
||||
|
||||
tmp = snd_ice1712_gpio_read(ice);
|
||||
|
||||
snd_ice1712_gpio_set_mask(ice, ~(AUREON_SPI_MOSI|AUREON_SPI_CLK|
|
||||
AUREON_WM_RW|AUREON_WM_CS|
|
||||
AUREON_CS8415_CS));
|
||||
tmp |= AUREON_WM_RW;
|
||||
tmp |= AUREON_CS8415_CS | AUREON_WM_CS; /* disable SPI devices */
|
||||
|
||||
tmp &= ~AUREON_SPI_MOSI;
|
||||
tmp &= ~AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(50);
|
||||
|
||||
/*
|
||||
* send i2c stop condition and start condition
|
||||
* to obtain sane state
|
||||
*/
|
||||
tmp |= AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(50);
|
||||
tmp |= AUREON_SPI_MOSI;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(100);
|
||||
tmp &= ~AUREON_SPI_MOSI;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(50);
|
||||
tmp &= ~AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(100);
|
||||
/*
|
||||
* send device address, command and value,
|
||||
* skipping ack cycles inbetween
|
||||
*/
|
||||
for (j = 0; j < 3; j++) {
|
||||
switch(j) {
|
||||
case 0: val = dev; break;
|
||||
case 1: val = reg; break;
|
||||
case 2: val = data; break;
|
||||
}
|
||||
for (i = 7; i >= 0; i--) {
|
||||
tmp &= ~AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
if (val & (1 << i))
|
||||
tmp |= AUREON_SPI_MOSI;
|
||||
else
|
||||
tmp &= ~AUREON_SPI_MOSI;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
tmp |= AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
}
|
||||
tmp &= ~AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
tmp |= AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
tmp &= ~AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
}
|
||||
tmp &= ~AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
tmp &= ~AUREON_SPI_MOSI;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(40);
|
||||
tmp |= AUREON_SPI_CLK;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(50);
|
||||
tmp |= AUREON_SPI_MOSI;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
char *texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"};
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 3;
|
||||
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_universe_inmux_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
unsigned char oval, nval;
|
||||
int change;
|
||||
|
||||
snd_ice1712_save_gpio_status(ice);
|
||||
|
||||
oval = ice->spec.aureon.pca9554_out;
|
||||
nval = ucontrol->value.integer.value[0];
|
||||
if ((change = (oval != nval))) {
|
||||
aureon_pca9554_write(ice, PCA9554_OUT, nval);
|
||||
ice->spec.aureon.pca9554_out = nval;
|
||||
}
|
||||
snd_ice1712_restore_gpio_status(ice);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
|
||||
unsigned short val)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
||||
/* Send address to XILINX chip */
|
||||
|
@ -146,7 +290,8 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
|
|||
/*
|
||||
* Initialize STAC9744 chip
|
||||
*/
|
||||
static int aureon_ac97_init (struct snd_ice1712 *ice) {
|
||||
static int aureon_ac97_init (struct snd_ice1712 *ice)
|
||||
{
|
||||
int i;
|
||||
static unsigned short ac97_defaults[] = {
|
||||
0x00, 0x9640,
|
||||
|
@ -1598,7 +1743,15 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
|
|||
.get = aureon_ac97_vol_get,
|
||||
.put = aureon_ac97_vol_put,
|
||||
.private_value = AC97_VIDEO|AUREON_AC97_STEREO
|
||||
}
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Aux Source",
|
||||
.info = aureon_universe_inmux_info,
|
||||
.get = aureon_universe_inmux_get,
|
||||
.put = aureon_universe_inmux_put
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -1856,6 +2009,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
|
|||
}
|
||||
|
||||
snd_ice1712_restore_gpio_status(ice);
|
||||
|
||||
/* initialize PCA9554 pin directions & set default input*/
|
||||
aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
|
||||
aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */
|
||||
|
||||
ice->spec.aureon.master[0] = WM_VOL_MUTE;
|
||||
ice->spec.aureon.master[1] = WM_VOL_MUTE;
|
||||
|
|
|
@ -373,6 +373,7 @@ struct snd_ice1712 {
|
|||
unsigned int cs8415_mux;
|
||||
unsigned short master[2];
|
||||
unsigned short vol[8];
|
||||
unsigned char pca9554_out;
|
||||
} aureon;
|
||||
/* AC97 register cache for Phase28 */
|
||||
struct phase28_spec {
|
||||
|
|
Loading…
Reference in a new issue