[ALSA] emu10k1: Add Audio capture support for Audigy 2 ZS Notebook.
Implement functionallity in order to fixe ALSA bug#2058. Signed-off-by: James Courtier-Dutton <James@superbug.co.uk> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
This commit is contained in:
parent
9ed1261e3e
commit
184c1e2c4c
|
@ -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
|
||||||
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 @@
|
||||||
#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[] = {
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
/* 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[] = {
|
||||||
.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[] = {
|
||||||
* 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[] = {
|
||||||
.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 @@
|
||||||
#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 =
|
||||||
.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,
|
||||||
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,
|
||||||
"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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
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,
|
||||||
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 @@
|
||||||
#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,
|
||||||
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 @@
|
||||||
#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 */
|
||||||
|
|
Loading…
Reference in New Issue