Commit a1c87c0b authored by Oswald Buddenhagen's avatar Oswald Buddenhagen Committed by Takashi Iwai

ALSA: emu10k1: fix access to Audigy GPIO port

As the register definition clearly states, this is a 16-bit register,
yet we did all accesses as 32-bit. The writes in particular would have
the potential to clear the TIMER register (depending on how the bus/card
actually handles the too long writes).

This commit also introduces a separate define A_GPIO which aliases
A_IOCFG, which better reflects the distinct usage on E-MU cards.
This is done in the same commit to keep the churn down, as we're
touching all involved lines anyway.
Signed-off-by: default avatarOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230421141006.1005539-2-oswald.buddenhagen@gmx.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 10f212bd
...@@ -251,11 +251,16 @@ ...@@ -251,11 +251,16 @@
#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ #define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */
#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ #define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */
#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ #define A_GPIO 0x18 /* GPIO on Audigy card (16bits) */
#define A_GPINPUT_MASK 0xff00 #define A_GPINPUT_MASK 0xff00
#define A_GPOUTPUT_MASK 0x00ff #define A_GPOUTPUT_MASK 0x00ff
// The GPIO port is used for I/O config on Sound Blasters;
// card-specific info can be found in the emu_chip_details table.
// On E-MU cards the port is used as the interface to the FPGA.
// Audigy output/GPIO stuff taken from the kX drivers // Audigy output/GPIO stuff taken from the kX drivers
#define A_IOCFG A_GPIO
#define A_IOCFG_GPOUT0 0x0044 /* analog/digital */ #define A_IOCFG_GPOUT0 0x0044 /* analog/digital */
#define A_IOCFG_DISABLE_ANALOG 0x0040 /* = 'enable' for Audigy2 (chiprev=4) */ #define A_IOCFG_DISABLE_ANALOG 0x0040 /* = 'enable' for Audigy2 (chiprev=4) */
#define A_IOCFG_ENABLE_DIGITAL 0x0004 #define A_IOCFG_ENABLE_DIGITAL 0x0004
......
...@@ -223,8 +223,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -223,8 +223,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
*/ */
outl(0x7a0000, emu->port + 0x20); outl(0x7a0000, emu->port + 0x20);
outl(0xFF000000, emu->port + 0x24); outl(0xFF000000, emu->port + 0x24);
tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */ tmp = inw(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
outl(tmp, emu->port + A_IOCFG); outw(tmp, emu->port + A_IOCFG);
} }
if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */ if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
int size, n; int size, n;
...@@ -244,15 +244,15 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -244,15 +244,15 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
* GPIO6: Unknown * GPIO6: Unknown
* GPIO7: Unknown * GPIO7: Unknown
*/ */
outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ outw(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
} }
if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
int size, n; int size, n;
snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f); snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
tmp = inl(emu->port + A_IOCFG); tmp = inw(emu->port + A_IOCFG);
outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */ outw(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
tmp = inl(emu->port + A_IOCFG); tmp = inw(emu->port + A_IOCFG);
size = ARRAY_SIZE(i2c_adc_init); size = ARRAY_SIZE(i2c_adc_init);
for (n = 0; n < size; n++) for (n = 0; n < size; n++)
snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]); snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
...@@ -308,12 +308,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -308,12 +308,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} else if (emu->card_capabilities->i2c_adc) { } else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { } else if (emu->audigy) {
unsigned int reg = inl(emu->port + A_IOCFG); u16 reg = inw(emu->port + A_IOCFG);
outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); outw(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
udelay(500); udelay(500);
outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG); outw(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
udelay(100); udelay(100);
outl(reg, emu->port + A_IOCFG); outw(reg, emu->port + A_IOCFG);
} else { } else {
unsigned int reg = inl(emu->port + HCFG); unsigned int reg = inl(emu->port + HCFG);
outl(reg | HCFG_GPOUT2, emu->port + HCFG); outl(reg | HCFG_GPOUT2, emu->port + HCFG);
...@@ -329,8 +329,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) ...@@ -329,8 +329,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} else if (emu->card_capabilities->i2c_adc) { } else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ ; /* 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); u16 reg = inw(emu->port + A_IOCFG);
outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); outw(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
} }
if (emu->address_mode == 0) { if (emu->address_mode == 0) {
...@@ -354,19 +354,19 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) ...@@ -354,19 +354,19 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
} else if (emu->card_capabilities->i2c_adc) { } else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ ; /* 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); outw(inw(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Unmute Analog now. Set GPO6 to 1 for Apollo. /* Unmute Analog now. Set GPO6 to 1 for Apollo.
* This has to be done after init ALice3 I2SOut beyond 48KHz. * This has to be done after init ALice3 I2SOut beyond 48KHz.
* So, sequence is important. */ * So, sequence is important. */
outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); outw(inw(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
} else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */ } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
/* Unmute Analog now. */ /* Unmute Analog now. */
outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG); outw(inw(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
} else { } else {
/* Disable routing from AC97 line out to Front speakers */ /* Disable routing from AC97 line out to Front speakers */
outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG); outw(inw(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
} }
} }
...@@ -651,9 +651,9 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, ...@@ -651,9 +651,9 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
const struct firmware *fw_entry) const struct firmware *fw_entry)
{ {
int n, i; int n, i;
int reg; u16 reg;
int value; u8 value;
__always_unused unsigned int write_post; __always_unused u16 write_post;
unsigned long flags; unsigned long flags;
if (!fw_entry) if (!fw_entry)
...@@ -666,11 +666,11 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, ...@@ -666,11 +666,11 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
* FPGA CONFIG OFF -> FPGA PGMN * FPGA CONFIG OFF -> FPGA PGMN
*/ */
spin_lock_irqsave(&emu->emu_lock, flags); spin_lock_irqsave(&emu->emu_lock, flags);
outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */ outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 1uS. */
write_post = inl(emu->port + A_IOCFG); write_post = inw(emu->port + A_GPIO);
udelay(100); udelay(100);
outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */ outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */
write_post = inl(emu->port + A_IOCFG); write_post = inw(emu->port + A_GPIO);
udelay(100); /* Allow FPGA memory to clean */ udelay(100); /* Allow FPGA memory to clean */
for (n = 0; n < fw_entry->size; n++) { for (n = 0; n < fw_entry->size; n++) {
value = fw_entry->data[n]; value = fw_entry->data[n];
...@@ -679,15 +679,15 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, ...@@ -679,15 +679,15 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
if (value & 0x1) if (value & 0x1)
reg = reg | 0x20; reg = reg | 0x20;
value = value >> 1; value = value >> 1;
outl(reg, emu->port + A_IOCFG); outw(reg, emu->port + A_GPIO);
write_post = inl(emu->port + A_IOCFG); write_post = inw(emu->port + A_GPIO);
outl(reg | 0x40, emu->port + A_IOCFG); outw(reg | 0x40, emu->port + A_GPIO);
write_post = inl(emu->port + A_IOCFG); write_post = inw(emu->port + A_GPIO);
} }
} }
/* After programming, set GPIO bit 4 high again. */ /* After programming, set GPIO bit 4 high again. */
outl(0x10, emu->port + A_IOCFG); outw(0x10, emu->port + A_GPIO);
write_post = inl(emu->port + A_IOCFG); write_post = inw(emu->port + A_GPIO);
spin_unlock_irqrestore(&emu->emu_lock, flags); spin_unlock_irqrestore(&emu->emu_lock, flags);
return 0; return 0;
...@@ -2053,7 +2053,7 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu) ...@@ -2053,7 +2053,7 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
*val = snd_emu10k1_ptr_read(emu, *reg, i); *val = snd_emu10k1_ptr_read(emu, *reg, i);
} }
if (emu->audigy) if (emu->audigy)
emu->saved_a_iocfg = inl(emu->port + A_IOCFG); emu->saved_a_iocfg = inw(emu->port + A_IOCFG);
emu->saved_hcfg = inl(emu->port + HCFG); emu->saved_hcfg = inl(emu->port + HCFG);
} }
...@@ -2080,7 +2080,7 @@ void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu) ...@@ -2080,7 +2080,7 @@ void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
/* resore for spdif */ /* resore for spdif */
if (emu->audigy) if (emu->audigy)
outl(emu->saved_a_iocfg, emu->port + A_IOCFG); outw(emu->saved_a_iocfg, emu->port + A_IOCFG);
outl(emu->saved_hcfg, emu->port + HCFG); outl(emu->saved_hcfg, emu->port + HCFG);
val = emu->saved_ptr; val = emu->saved_ptr;
......
...@@ -924,7 +924,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, ...@@ -924,7 +924,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int source_id; unsigned int source_id;
unsigned int ngain, ogain; unsigned int ngain, ogain;
u32 gpio; u16 gpio;
int change = 0; int change = 0;
unsigned long flags; unsigned long flags;
u32 source; u32 source;
...@@ -941,11 +941,11 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, ...@@ -941,11 +941,11 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
if (change) { if (change) {
snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */ snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
spin_lock_irqsave(&emu->emu_lock, flags); spin_lock_irqsave(&emu->emu_lock, flags);
gpio = inl(emu->port + A_IOCFG); gpio = inw(emu->port + A_IOCFG);
if (source_id==0) if (source_id==0)
outl(gpio | 0x4, emu->port + A_IOCFG); outw(gpio | 0x4, emu->port + A_IOCFG);
else else
outl(gpio & ~0x4, emu->port + A_IOCFG); outw(gpio & ~0x4, emu->port + A_IOCFG);
spin_unlock_irqrestore(&emu->emu_lock, flags); spin_unlock_irqrestore(&emu->emu_lock, flags);
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
...@@ -1632,7 +1632,7 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol, ...@@ -1632,7 +1632,7 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
if (emu->audigy) if (emu->audigy)
ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0; ucontrol->value.integer.value[0] = inw(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
else else
ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0; ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
if (emu->card_capabilities->invert_shared_spdif) if (emu->card_capabilities->invert_shared_spdif)
...@@ -1657,13 +1657,13 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, ...@@ -1657,13 +1657,13 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
if ( emu->card_capabilities->i2c_adc) { if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */ /* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) { } else if (emu->audigy) {
reg = inl(emu->port + A_IOCFG); reg = inw(emu->port + A_IOCFG);
val = sw ? A_IOCFG_GPOUT0 : 0; val = sw ? A_IOCFG_GPOUT0 : 0;
change = (reg & A_IOCFG_GPOUT0) != val; change = (reg & A_IOCFG_GPOUT0) != val;
if (change) { if (change) {
reg &= ~A_IOCFG_GPOUT0; reg &= ~A_IOCFG_GPOUT0;
reg |= val; reg |= val;
outl(reg | val, emu->port + A_IOCFG); outw(reg | val, emu->port + A_IOCFG);
} }
} }
reg = inl(emu->port + HCFG); reg = inl(emu->port + HCFG);
......
...@@ -243,13 +243,13 @@ void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) ...@@ -243,13 +243,13 @@ void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */ if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
return; return;
spin_lock_irqsave(&emu->emu_lock, flags); spin_lock_irqsave(&emu->emu_lock, flags);
outl(reg, emu->port + A_IOCFG); outw(reg, emu->port + A_GPIO);
udelay(10); udelay(10);
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10); udelay(10);
outl(value, emu->port + A_IOCFG); outw(value, emu->port + A_GPIO);
udelay(10); udelay(10);
outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
spin_unlock_irqrestore(&emu->emu_lock, flags); spin_unlock_irqrestore(&emu->emu_lock, flags);
} }
...@@ -260,11 +260,11 @@ void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) ...@@ -260,11 +260,11 @@ void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
return; return;
reg += 0x40; /* 0x40 upwards are registers. */ reg += 0x40; /* 0x40 upwards are registers. */
spin_lock_irqsave(&emu->emu_lock, flags); spin_lock_irqsave(&emu->emu_lock, flags);
outl(reg, emu->port + A_IOCFG); outw(reg, emu->port + A_GPIO);
udelay(10); udelay(10);
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10); udelay(10);
*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); *value = ((inw(emu->port + A_GPIO) >> 8) & 0x7f);
spin_unlock_irqrestore(&emu->emu_lock, flags); spin_unlock_irqrestore(&emu->emu_lock, flags);
} }
......
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