summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorJames Courtier-Dutton <James@superbug.co.uk>2007-11-10 17:55:14 +0000
committerJaroslav Kysela <perex@perex.cz>2008-01-31 17:30:24 +0100
commitc94fa4c9168e51a8dab8e72cb9f0d89673fc8d8c (patch)
tree08169553a3b69a284b322941131f406661e499e8 /sound
parent3839e4f136d6da3dc85d237aa9569ee94bfea763 (diff)
downloadlinux-c94fa4c9168e51a8dab8e72cb9f0d89673fc8d8c.tar.gz
linux-c94fa4c9168e51a8dab8e72cb9f0d89673fc8d8c.tar.bz2
linux-c94fa4c9168e51a8dab8e72cb9f0d89673fc8d8c.zip
[ALSA] emu10k1: General cleanup, add new locks, fix alsa bug#3501, kernel bug#9304.
Signed-off-by: James Courtier-Dutton <James@superbug.co.uk> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c15
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c6
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c45
-rw-r--r--sound/pci/emu10k1/io.c52
-rw-r--r--sound/pci/emu10k1/irq.c8
5 files changed, 80 insertions, 46 deletions
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 01965bd99966..45088ebcce50 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -35,9 +35,9 @@ struct best_voice {
/*
* prototypes
*/
-static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
+static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
struct best_voice *best, int active_only);
-static struct snd_emux_voice *get_voice(struct snd_emux *emu,
+static struct snd_emux_voice *get_voice(struct snd_emux *emux,
struct snd_emux_port *port);
static int start_voice(struct snd_emux_voice *vp);
static void trigger_voice(struct snd_emux_voice *vp);
@@ -45,7 +45,6 @@ static void release_voice(struct snd_emux_voice *vp);
static void update_voice(struct snd_emux_voice *vp, int update);
static void terminate_voice(struct snd_emux_voice *vp);
static void free_voice(struct snd_emux_voice *vp);
-
static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
@@ -75,9 +74,9 @@ static struct snd_emux_operators emu10k1_ops = {
};
void
-snd_emu10k1_ops_setup(struct snd_emux *emu)
+snd_emu10k1_ops_setup(struct snd_emux *emux)
{
- emu->ops = emu10k1_ops;
+ emux->ops = emu10k1_ops;
}
@@ -166,7 +165,11 @@ free_voice(struct snd_emux_voice *vp)
struct snd_emu10k1 *hw;
hw = vp->hw;
- if (vp->ch >= 0) {
+ /* FIXME: emu10k1_synth is broken. */
+ /* This can get called with hw == 0 */
+ /* Problem apparent on plug, unplug then plug */
+ /* on the Audigy 2 ZS Notebook. */
+ if (hw && (vp->ch >= 0)) {
snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 7e46325974a5..f29caf1afe06 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -259,7 +259,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
* GPIO7: Unknown
*/
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;
@@ -275,7 +274,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
emu->i2c_capture_volume[n][0]= 0xcf;
emu->i2c_capture_volume[n][1]= 0xcf;
}
-
}
@@ -653,6 +651,8 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
value = inl(special_port);
snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
+ /* Delay to give time for ADC chip to switch on. It needs 113ms */
+ msleep(200);
return 0;
}
@@ -1717,6 +1717,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->card = card;
spin_lock_init(&emu->reg_lock);
spin_lock_init(&emu->emu_lock);
+ spin_lock_init(&emu->spi_lock);
+ spin_lock_init(&emu->i2c_lock);
spin_lock_init(&emu->voice_lock);
spin_lock_init(&emu->synth_lock);
spin_lock_init(&emu->memblk_lock);
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 204995a1dfbd..ad7b71491fc4 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -30,7 +30,7 @@ MODULE_LICENSE("GPL");
*/
static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
{
- struct snd_emux *emu;
+ struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
unsigned long flags;
@@ -46,53 +46,56 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
else if (arg->max_voices > 64)
arg->max_voices = 64;
- if (snd_emux_new(&emu) < 0)
+ if (snd_emux_new(&emux) < 0)
return -ENOMEM;
- snd_emu10k1_ops_setup(emu);
- emu->hw = hw = arg->hwptr;
- emu->max_voices = arg->max_voices;
- emu->num_ports = arg->seq_ports;
- emu->pitch_shift = -501;
- emu->memhdr = hw->memhdr;
- emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
- emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
- emu->linear_panning = 0;
- emu->hwdep_idx = 2; /* FIXED */
-
- if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
- snd_emux_free(emu);
+ snd_emu10k1_ops_setup(emux);
+ hw = arg->hwptr;
+ emux->hw = hw;
+ emux->max_voices = arg->max_voices;
+ emux->num_ports = arg->seq_ports;
+ emux->pitch_shift = -501;
+ emux->memhdr = hw->memhdr;
+ /* maximum two ports */
+ emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
+ /* audigy has two external midis */
+ emux->midi_devidx = hw->audigy ? 2 : 1;
+ emux->linear_panning = 0;
+ emux->hwdep_idx = 2; /* FIXED */
+
+ if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) {
+ snd_emux_free(emux);
return -ENOMEM;
}
spin_lock_irqsave(&hw->voice_lock, flags);
- hw->synth = emu;
+ hw->synth = emux;
hw->get_synth_voice = snd_emu10k1_synth_get_voice;
spin_unlock_irqrestore(&hw->voice_lock, flags);
- dev->driver_data = emu;
+ dev->driver_data = emux;
return 0;
}
static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
{
- struct snd_emux *emu;
+ struct snd_emux *emux;
struct snd_emu10k1 *hw;
unsigned long flags;
if (dev->driver_data == NULL)
return 0; /* not registered actually */
- emu = dev->driver_data;
+ emux = dev->driver_data;
- hw = emu->hw;
+ hw = emux->hw;
spin_lock_irqsave(&hw->voice_lock, flags);
hw->synth = NULL;
hw->get_synth_voice = NULL;
spin_unlock_irqrestore(&hw->voice_lock, flags);
- snd_emux_free(emu);
+ snd_emux_free(emux);
return 0;
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index a02638350a0a..b5a802bdeb7c 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -70,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
unsigned long flags;
unsigned int mask;
+ if (!emu) {
+ snd_printk(KERN_ERR "ptr_write: emu is null!\n");
+ dump_stack();
+ return;
+ }
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
@@ -134,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
unsigned int reset, set;
unsigned int reg, tmp;
int n, result;
+ int err = 0;
+
+ /* This function is not re-entrant, so protect against it. */
+ spin_lock(&emu->spi_lock);
if (emu->card_capabilities->ca0108_chip)
reg = 0x3c; /* PTR20, reg 0x3c */
else {
/* For other chip types the SPI register
* is currently unknown. */
- return 1;
+ err = 1;
+ goto spi_write_exit;
+ }
+ if (data > 0xffff) {
+ /* Only 16bit values allowed */
+ err = 1;
+ goto spi_write_exit;
}
- if (data > 0xffff) /* Only 16bit values allowed */
- return 1;
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
@@ -160,11 +173,17 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
break;
}
}
- if (result) /* Timed out */
- return 1;
+ if (result) {
+ /* Timed out */
+ err = 1;
+ goto spi_write_exit;
+ }
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
- return 0;
+ err = 0;
+spi_write_exit:
+ spin_unlock(&emu->spi_lock);
+ return err;
}
/* The ADC does not support i2c read, so only write is implemented */
@@ -176,15 +195,17 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
int timeout = 0;
int status;
int retry;
+ int err = 0;
+
if ((reg > 0x7f) || (value > 0x1ff)) {
snd_printk(KERN_ERR "i2c_write: invalid values.\n");
return -EINVAL;
}
+ /* This function is not re-entrant, so protect against it. */
+ spin_lock(&emu->i2c_lock);
+
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);
@@ -192,17 +213,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
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);
+ mdelay(1);
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;
@@ -219,10 +237,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
if (retry == 10) {
snd_printk(KERN_ERR "Writing to ADC failed!\n");
- return -EINVAL;
+ snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
+ status, reg, value);
+ /* dump_stack(); */
+ err = -EINVAL;
}
- return 0;
+ spin_unlock(&emu->i2c_lock);
+ return err;
}
int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index 276d08c88f91..30bfed6f8339 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -34,9 +34,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
struct snd_emu10k1 *emu = dev_id;
unsigned int status, status2, orig_status, orig_status2;
int handled = 0;
+ int timeout = 0;
- while ((status = inl(emu->port + IPR)) != 0) {
- //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status);
+ while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
+ timeout++;
orig_status = status;
handled = 1;
if ((status & 0xffffffff) == 0xffffffff) {
@@ -200,5 +201,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
}
outl(orig_status, emu->port + IPR); /* ack all */
}
+ if (timeout == 1000)
+ snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
+
return IRQ_RETVAL(handled);
}