summary refs log tree commit diff
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
[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);
 }