[ALSA] emu10k1: General cleanup, add new locks, fix alsa bug#3501, kernel bug#9304.
authorJames Courtier-Dutton <James@superbug.co.uk>
Sat, 10 Nov 2007 17:55:14 +0000 (17:55 +0000)
committerJaroslav Kysela <perex@perex.cz>
Thu, 31 Jan 2008 16:30:24 +0000 (17:30 +0100)
Signed-off-by: James Courtier-Dutton <James@superbug.co.uk>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
include/sound/emu10k1.h
sound/pci/emu10k1/emu10k1_callback.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emu10k1_synth.c
sound/pci/emu10k1/io.c
sound/pci/emu10k1/irq.c

index 494648dd7073785a018763bdca96171dc224e128..7b7b9b13b4ddd8d04c1cf6392d15c95d58972de0 100644 (file)
@@ -1742,6 +1742,8 @@ struct snd_emu10k1 {
        spinlock_t reg_lock;
        spinlock_t emu_lock;
        spinlock_t voice_lock;
+       spinlock_t spi_lock; /* serialises access to spi port */
+       spinlock_t i2c_lock; /* serialises access to i2c port */
 
        struct snd_emu10k1_voice voices[NUM_G];
        struct snd_emu10k1_voice p16v_voices[4];
index 01965bd99966e0e76e13cbc0d90683b86bb4492a..45088ebcce5041a9b25e147d41be6add78351f05 100644 (file)
@@ -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);
index 7e46325974a55b3283bc72bdad18d6623c84322f..f29caf1afe06503496873bf815f0e21124a244f0 100644 (file)
@@ -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);
index 204995a1dfbd15e311f0dbce1360b7529b4a5f95..ad7b71491fc49cd32a9d08f8abbf3c5e4938485e 100644 (file)
@@ -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;
 }
 
index a02638350a0ac40ad37f144a727c1d351bf2fc47..b5a802bdeb7c1b21f76573e72bbd16fd2af74898 100644 (file)
@@ -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)
index 276d08c88f9109c831f7deca69d933a8a4969ccd..30bfed6f83398d5f9b17fdf776ce2ab6ebac7a4e 100644 (file)
@@ -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);
 }