Commit 184c1e2c authored by James Courtier-Dutton's avatar James Courtier-Dutton Committed by Jaroslav Kysela

[ALSA] emu10k1: Add Audio capture support for Audigy 2 ZS Notebook.

Implement functionallity in order to fixe ALSA bug#2058.
Signed-off-by: default avatarJames Courtier-Dutton <James@superbug.co.uk>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent 9ed1261e
...@@ -1427,6 +1427,8 @@ struct snd_emu10k1 { ...@@ -1427,6 +1427,8 @@ struct snd_emu10k1 {
spinlock_t memblk_lock; spinlock_t memblk_lock;
unsigned int spdif_bits[3]; /* s/pdif out setup */ unsigned int spdif_bits[3]; /* s/pdif out setup */
unsigned int i2c_capture_source;
u8 i2c_capture_volume[4][2];
struct snd_emu10k1_fx8010 fx8010; /* FX8010 info */ struct snd_emu10k1_fx8010 fx8010; /* FX8010 info */
int gpr_base; int gpr_base;
...@@ -1532,6 +1534,7 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i ...@@ -1532,6 +1534,7 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn); unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn);
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data); void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data); int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value); int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value);
int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value); int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value);
int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src); int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src);
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include "p16v.h" #include "p16v.h"
#include "tina2.h" #include "tina2.h"
#include "p17v.h"
/************************************************************************* /*************************************************************************
...@@ -121,10 +122,27 @@ static unsigned int spi_dac_init[] = { ...@@ -121,10 +122,27 @@ static unsigned int spi_dac_init[] = {
0x1400, 0x1400,
}; };
static unsigned int i2c_adc_init[][2] = {
{ 0x17, 0x00 }, /* Reset */
{ 0x07, 0x00 }, /* Timeout */
{ 0x0b, 0x22 }, /* Interface control */
{ 0x0c, 0x22 }, /* Master mode control */
{ 0x0d, 0x08 }, /* Powerdown control */
{ 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */
{ 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */
{ 0x10, 0x7b }, /* ALC Control 1 */
{ 0x11, 0x00 }, /* ALC Control 2 */
{ 0x12, 0x32 }, /* ALC Control 3 */
{ 0x13, 0x00 }, /* Noise gate control */
{ 0x14, 0xa6 }, /* Limiter control */
{ 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */
};
static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
{ {
unsigned int silent_page; unsigned int silent_page;
int ch; int ch;
u32 tmp;
/* disable audio and lock cache */ /* disable audio and lock cache */
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE,
...@@ -163,8 +181,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -163,8 +181,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Hacks for Alice3 to work independent of haP16V driver */ /* Hacks for Alice3 to work independent of haP16V driver */
u32 tmp;
//Setup SRCMulti_I2S SamplingRate //Setup SRCMulti_I2S SamplingRate
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp &= 0xfffff1ff; tmp &= 0xfffff1ff;
...@@ -184,8 +200,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -184,8 +200,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} }
if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */ if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
/* Hacks for Alice3 to work independent of haP16V driver */ /* Hacks for Alice3 to work independent of haP16V driver */
u32 tmp;
snd_printk(KERN_INFO "Audigy2 value: Special config.\n"); snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
//Setup SRCMulti_I2S SamplingRate //Setup SRCMulti_I2S SamplingRate
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
...@@ -231,6 +245,23 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -231,6 +245,23 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
} }
if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
int size, n;
snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
tmp = inl(emu->port + A_IOCFG);
outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
tmp = inl(emu->port + A_IOCFG);
size = ARRAY_SIZE(i2c_adc_init);
for (n = 0; n < size; n++)
snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
for (n=0; n < 4; n++) {
emu->i2c_capture_volume[n][0]= 0xcf;
emu->i2c_capture_volume[n][1]= 0xcf;
}
}
snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr); snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */
...@@ -274,6 +305,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -274,6 +305,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
if (enable_ir) { /* enable IR for SB Live */ if (enable_ir) { /* enable IR for SB Live */
if (emu->card_capabilities->emu1010) { if (emu->card_capabilities->emu1010) {
; /* Disable all access to A_IOCFG for the emu1010 */ ; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { } else if (emu->audigy) {
unsigned int reg = inl(emu->port + A_IOCFG); unsigned int reg = inl(emu->port + A_IOCFG);
outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
...@@ -293,6 +326,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -293,6 +326,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
if (emu->card_capabilities->emu1010) { if (emu->card_capabilities->emu1010) {
; /* Disable all access to A_IOCFG for the emu1010 */ ; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { /* enable analog output */ } else if (emu->audigy) { /* enable analog output */
unsigned int reg = inl(emu->port + A_IOCFG); unsigned int reg = inl(emu->port + A_IOCFG);
outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
...@@ -311,6 +346,8 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) ...@@ -311,6 +346,8 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
/* Enable analog/digital outs on audigy */ /* Enable analog/digital outs on audigy */
if (emu->card_capabilities->emu1010) { if (emu->card_capabilities->emu1010) {
; /* Disable all access to A_IOCFG for the emu1010 */ ; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { } else if (emu->audigy) {
outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
...@@ -1139,10 +1176,11 @@ static struct snd_emu_chip_details emu_chip_details[] = { ...@@ -1139,10 +1176,11 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.adc_1361t = 1, /* 24 bit capture instead of 16bit */ .adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} , .ac97_chip = 1} ,
/* Audigy 2 ZS Notebook Cardbus card.*/ /* Audigy 2 ZS Notebook Cardbus card.*/
/* Tested by James@superbug.co.uk 22th December 2005 */ /* Tested by James@superbug.co.uk 6th November 2006 */
/* Audio output 7.1/Headphones working. /* Audio output 7.1/Headphones working.
* Digital output working. (AC3 not checked, only PCM) * Digital output working. (AC3 not checked, only PCM)
* Audio inputs not tested. * Audio Mic/Line inputs working.
* Digital input not tested.
*/ */
/* DSP: Tina2 /* DSP: Tina2
* DAC: Wolfson WM8768/WM8568 * DAC: Wolfson WM8768/WM8568
...@@ -1150,6 +1188,25 @@ static struct snd_emu_chip_details emu_chip_details[] = { ...@@ -1150,6 +1188,25 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* AC97: None * AC97: None
* CA0151: None * CA0151: None
*/ */
/* Tested by James@superbug.co.uk 4th April 2006 */
/* A_IOCFG bits
* Output
* 0: Not Used
* 1: 0 = Mute all the 7.1 channel out. 1 = unmute.
* 2: Analog input 0 = line in, 1 = mic in
* 3: Not Used
* 4: Digital output 0 = off, 1 = on.
* 5: Not Used
* 6: Not Used
* 7: Not Used
* Input
* All bits 1 (0x3fxx) means nothing plugged in.
* 8-9: 0 = Line in/Mic, 2 = Optical in, 3 = Nothing.
* A-B: 0 = Headphones, 2 = Optical out, 3 = Nothing.
* C-D: 2 = Front/Rear/etc, 3 = nothing.
* E-F: Always 0
*
*/
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102, {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
.driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
.id = "Audigy2", .id = "Audigy2",
...@@ -1157,6 +1214,7 @@ static struct snd_emu_chip_details emu_chip_details[] = { ...@@ -1157,6 +1214,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0108_chip = 1, .ca0108_chip = 1,
.ca_cardbus_chip = 1, .ca_cardbus_chip = 1,
.spi_dac = 1, .spi_dac = 1,
.i2c_adc = 1,
.spk71 = 1} , .spk71 = 1} ,
{.vendor = 0x1102, .device = 0x0008, {.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
......
...@@ -36,9 +36,14 @@ ...@@ -36,9 +36,14 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/emu10k1.h> #include <sound/emu10k1.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <sound/tlv.h>
#include "p17v.h"
#define AC97_ID_STAC9758 0x83847658 #define AC97_ID_STAC9758 0x83847658
static DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
...@@ -579,6 +584,162 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock = ...@@ -579,6 +584,162 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock =
.put = snd_emu1010_internal_clock_put .put = snd_emu1010_internal_clock_put
}; };
static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
#if 0
static char *texts[4] = {
"Unknown1", "Unknown2", "Mic", "Line"
};
#endif
static char *texts[2] = {
"Mic", "Line"
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item > 1)
uinfo->value.enumerated.item = 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
return 0;
}
static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int source_id;
unsigned int ngain, ogain;
u32 gpio;
int change = 0;
unsigned long flags;
u32 source;
/* If the capture source has changed,
* update the capture volume from the cached value
* for the particular source.
*/
source_id = ucontrol->value.enumerated.item[0]; /* Use 2 and 3 */
change = (emu->i2c_capture_source != source_id);
if (change) {
snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
spin_lock_irqsave(&emu->emu_lock, flags);
gpio = inl(emu->port + A_IOCFG);
if (source_id==0)
outl(gpio | 0x4, emu->port + A_IOCFG);
else
outl(gpio & ~0x4, emu->port + A_IOCFG);
spin_unlock_irqrestore(&emu->emu_lock, flags);
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
if (ngain != ogain)
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
ngain = emu->i2c_capture_volume[source_id][1]; /* Right */
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
if (ngain != ogain)
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
source = 1 << (source_id + 2);
snd_emu10k1_i2c_write(emu, ADC_MUX, source); /* Set source */
emu->i2c_capture_source = source_id;
}
return change;
}
static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = snd_audigy_i2c_capture_source_info,
.get = snd_audigy_i2c_capture_source_get,
.put = snd_audigy_i2c_capture_source_put
};
static int snd_audigy_i2c_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 255;
return 0;
}
static int snd_audigy_i2c_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int source_id;
source_id = kcontrol->private_value;
ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
return 0;
}
static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int ogain;
unsigned int ngain;
int source_id;
int change = 0;
source_id = kcontrol->private_value;
ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
ngain = ucontrol->value.integer.value[0];
if (ngain > 0xff)
return 0;
if (ogain != ngain) {
if (emu->i2c_capture_source == source_id)
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
change = 1;
}
ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
ngain = ucontrol->value.integer.value[1];
if (ngain > 0xff)
return 0;
if (ogain != ngain) {
if (emu->i2c_capture_source == source_id)
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
change = 1;
}
return change;
}
#define I2C_VOLUME(xname,chid) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_audigy_i2c_volume_info, \
.get = snd_audigy_i2c_volume_get, \
.put = snd_audigy_i2c_volume_put, \
.tlv = { .p = snd_audigy_db_scale2 }, \
.private_value = chid \
}
static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = {
I2C_VOLUME("Mic Capture Volume", 0),
I2C_VOLUME("Line Capture Volume", 0)
};
#if 0 #if 0
static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
...@@ -1179,7 +1340,9 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, ...@@ -1179,7 +1340,9 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
int change = 0; int change = 0;
spin_lock_irqsave(&emu->reg_lock, flags); spin_lock_irqsave(&emu->reg_lock, flags);
if (emu->audigy) { if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
reg = inl(emu->port + A_IOCFG); reg = inl(emu->port + A_IOCFG);
val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0; val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
change = (reg & A_IOCFG_GPOUT0) != val; change = (reg & A_IOCFG_GPOUT0) != val;
...@@ -1317,6 +1480,22 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, ...@@ -1317,6 +1480,22 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"AMic Playback Volume", "Mic Playback Volume", "AMic Playback Volume", "Mic Playback Volume",
NULL NULL
}; };
static char *audigy_rename_ctls_i2c_adc[] = {
//"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
"Line Capture Volume", "Analog Mix Capture Volume",
"Wave Playback Volume", "OLD PCM Playback Volume",
"Wave Master Playback Volume", "Master Playback Volume",
"AMic Playback Volume", "Old Mic Playback Volume",
NULL
};
static char *audigy_remove_ctls_i2c_adc[] = {
/* On the Audigy2 ZS Notebook
* Capture via WM8775 */
"Mic Capture Volume",
"Analog Mix Capture Volume",
"Aux Capture Volume",
NULL
};
static char *audigy_remove_ctls_1361t_adc[] = { static char *audigy_remove_ctls_1361t_adc[] = {
/* On the Audigy2 the AC97 playback is piped into /* On the Audigy2 the AC97 playback is piped into
* the Philips ADC for 24bit capture */ * the Philips ADC for 24bit capture */
...@@ -1409,6 +1588,10 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, ...@@ -1409,6 +1588,10 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
} }
for (; *c; c++) for (; *c; c++)
remove_ctl(card, *c); remove_ctl(card, *c);
} else if (emu->card_capabilities->i2c_adc) {
c = audigy_remove_ctls_i2c_adc;
for (; *c; c++)
remove_ctl(card, *c);
} else { } else {
no_ac97: no_ac97:
if (emu->card_capabilities->ecard) if (emu->card_capabilities->ecard)
...@@ -1422,6 +1605,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, ...@@ -1422,6 +1605,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
if (emu->audigy) if (emu->audigy)
if (emu->card_capabilities->adc_1361t) if (emu->card_capabilities->adc_1361t)
c = audigy_rename_ctls_1361t_adc; c = audigy_rename_ctls_1361t_adc;
else if (emu->card_capabilities->i2c_adc)
c = audigy_rename_ctls_i2c_adc;
else else
c = audigy_rename_ctls; c = audigy_rename_ctls;
else else
...@@ -1585,5 +1770,19 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, ...@@ -1585,5 +1770,19 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
return err; return err;
} }
if ( emu->card_capabilities->i2c_adc) {
int i;
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
if (err < 0)
return err;
for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) {
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu));
if (err < 0)
return err;
}
}
return 0; return 0;
} }
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/emu10k1.h> #include <sound/emu10k1.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "p17v.h"
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn) unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
{ {
...@@ -167,6 +168,64 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, ...@@ -167,6 +168,64 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
return 0; return 0;
} }
/* The ADC does not support i2c read, so only write is implemented */
int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
u32 reg,
u32 value)
{
u32 tmp;
int timeout = 0;
int status;
int retry;
if ((reg > 0x7f) || (value > 0x1ff)) {
snd_printk(KERN_ERR "i2c_write: invalid values.\n");
return -EINVAL;
}
tmp = reg << 25 | value << 16;
// snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
/* Not sure what this I2C channel controls. */
/* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */
/* This controls the I2C connected to the WM8775 ADC Codec */
snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
for (retry = 0; retry < 10; retry++) {
/* Send the data to i2c */
//tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0);
//tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
tmp = 0;
tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
/* Wait till the transaction ends */
while (1) {
udelay(10);
status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
// snd_printk("I2C:status=0x%x\n", status);
timeout++;
if ((status & I2C_A_ADC_START) == 0)
break;
if (timeout > 1000) {
snd_printk("emu10k1:I2C:timeout status=0x%x\n", status);
break;
}
}
//Read back and see if the transaction is successful
if ((status & I2C_A_ADC_ABORT) == 0)
break;
}
if (retry == 10) {
snd_printk(KERN_ERR "Writing to ADC failed!\n");
return -EINVAL;
}
return 0;
}
int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value) int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
{ {
if (reg < 0 || reg > 0x3f) if (reg < 0 || reg > 0x3f)
......
...@@ -43,6 +43,53 @@ ...@@ -43,6 +43,53 @@
#define P17V_I2C_ADDR 0x3d /* I2C Address */ #define P17V_I2C_ADDR 0x3d /* I2C Address */
#define P17V_I2C_0 0x3e /* I2C Data */ #define P17V_I2C_0 0x3e /* I2C Data */
#define P17V_I2C_1 0x3f /* I2C Data */ #define P17V_I2C_1 0x3f /* I2C Data */
/* I2C values */
#define I2C_A_ADC_ADD_MASK 0x000000fe /*The address is a 7 bit address */
#define I2C_A_ADC_RW_MASK 0x00000001 /*bit mask for R/W */
#define I2C_A_ADC_TRANS_MASK 0x00000010 /*Bit mask for I2c address DAC value */
#define I2C_A_ADC_ABORT_MASK 0x00000020 /*Bit mask for I2C transaction abort flag */
#define I2C_A_ADC_LAST_MASK 0x00000040 /*Bit mask for Last word transaction */
#define I2C_A_ADC_BYTE_MASK 0x00000080 /*Bit mask for Byte Mode */
#define I2C_A_ADC_ADD 0x00000034 /*This is the Device address for ADC */
#define I2C_A_ADC_READ 0x00000001 /*To perform a read operation */
#define I2C_A_ADC_START 0x00000100 /*Start I2C transaction */
#define I2C_A_ADC_ABORT 0x00000200 /*I2C transaction abort */
#define I2C_A_ADC_LAST 0x00000400 /*I2C last transaction */
#define I2C_A_ADC_BYTE 0x00000800 /*I2C one byte mode */
#define I2C_D_ADC_REG_MASK 0xfe000000 /*ADC address register */
#define I2C_D_ADC_DAT_MASK 0x01ff0000 /*ADC data register */
#define ADC_TIMEOUT 0x00000007 /*ADC Timeout Clock Disable */
#define ADC_IFC_CTRL 0x0000000b /*ADC Interface Control */
#define ADC_MASTER 0x0000000c /*ADC Master Mode Control */
#define ADC_POWER 0x0000000d /*ADC PowerDown Control */
#define ADC_ATTEN_ADCL 0x0000000e /*ADC Attenuation ADCL */
#define ADC_ATTEN_ADCR 0x0000000f /*ADC Attenuation ADCR */
#define ADC_ALC_CTRL1 0x00000010 /*ADC ALC Control 1 */
#define ADC_ALC_CTRL2 0x00000011 /*ADC ALC Control 2 */
#define ADC_ALC_CTRL3 0x00000012 /*ADC ALC Control 3 */
#define ADC_NOISE_CTRL 0x00000013 /*ADC Noise Gate Control */
#define ADC_LIMIT_CTRL 0x00000014 /*ADC Limiter Control */
#define ADC_MUX 0x00000015 /*ADC Mux offset */
#if 0
/* FIXME: Not tested yet. */
#define ADC_GAIN_MASK 0x000000ff //Mask for ADC Gain
#define ADC_ZERODB 0x000000cf //Value to set ADC to 0dB
#define ADC_MUTE_MASK 0x000000c0 //Mask for ADC mute
#define ADC_MUTE 0x000000c0 //Value to mute ADC
#define ADC_OSR 0x00000008 //Mask for ADC oversample rate select
#define ADC_TIMEOUT_DISABLE 0x00000008 //Value and mask to disable Timeout clock
#define ADC_HPF_DISABLE 0x00000100 //Value and mask to disable High pass filter
#define ADC_TRANWIN_MASK 0x00000070 //Mask for Length of Transient Window
#endif
#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux
#define ADC_MUX_0 0x00000001 //Value to select Unknown at ADC Mux (Not used)
#define ADC_MUX_1 0x00000002 //Value to select Unknown at ADC Mux (Not used)
#define ADC_MUX_2 0x00000004 //Value to select Mic at ADC Mux
#define ADC_MUX_3 0x00000008 //Value to select Line-In at ADC Mux
#define P17V_START_AUDIO 0x40 /* Start Audio bit */ #define P17V_START_AUDIO 0x40 /* Start Audio bit */
/* 41 - 47: Reserved */ /* 41 - 47: Reserved */
......
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