[ALSA] ice1724 - Fix IRQ lock-up with MPU access
authorTakashi Iwai <tiwai@suse.de>
Wed, 23 Apr 2008 15:47:28 +0000 (17:47 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 24 Apr 2008 10:38:25 +0000 (12:38 +0200)
The sound boards with VT1724 and compatible chips may lock up when
MPU401 is accessed together with the PCM streaming.
This patch fixes the problem.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/ice1712/ice1724.c
sound/pci/ice1712/prodigy192.c

index 13ea94f..4490422 100644 (file)
@@ -222,6 +222,32 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
        return data;
 }
 
+/*
+ * MPU401 accessor
+ */
+static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
+                                           unsigned long addr)
+{
+       /* fix status bits to the standard position */
+       /* only RX_EMPTY and TX_FULL are checked */
+       if (addr == MPU401C(mpu))
+               return (inb(addr) & 0x0c) << 4;
+       else
+               return inb(addr);
+}
+
+static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
+                                   unsigned char data, unsigned long addr)
+{
+       if (addr == MPU401C(mpu)) {
+               if (data == MPU401_ENTER_UART)
+                       outb(0x01, addr);
+               /* what else? */
+       } else
+               outb(data, addr);
+}
+
+
 /*
  *  Interrupt handler
  */
@@ -230,24 +256,53 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
 {
        struct snd_ice1712 *ice = dev_id;
        unsigned char status;
+       unsigned char status_mask =
+               VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM;
        int handled = 0;
+#ifdef CONFIG_SND_DEBUG
+       int timeout = 0;
+#endif
 
        while (1) {
                status = inb(ICEREG1724(ice, IRQSTAT));
+               status &= status_mask;
                if (status == 0)
                        break;
-
+#ifdef CONFIG_SND_DEBUG
+               if (++timeout > 10) {
+                       printk(KERN_ERR
+                              "ice1724: Too long irq loop, status = 0x%x\n",
+                              status);
+                       break;
+               }
+#endif
                handled = 1;            
-               /* these should probably be separated at some point, 
-                * but as we don't currently have MPU support on the board
-                * I will leave it
-                */
-               if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) {
+               if (status & VT1724_IRQ_MPU_TX) {
                        if (ice->rmidi[0])
-                               snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data);
-                       outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT));
-                       status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);
+                               snd_mpu401_uart_interrupt_tx(irq,
+                                       ice->rmidi[0]->private_data);
+                       else /* disable TX to be sure */
+                               outb(inb(ICEREG1724(ice, IRQMASK)) |
+                                    VT1724_IRQ_MPU_TX,
+                                    ICEREG1724(ice, IRQMASK));
+                       /* Due to mysterical reasons, MPU_TX is always
+                        * generated (and can't be cleared) when a PCM
+                        * playback is going.  So let's ignore at the
+                        * next loop.
+                        */
+                       status_mask &= ~VT1724_IRQ_MPU_TX;
+               }
+               if (status & VT1724_IRQ_MPU_RX) {
+                       if (ice->rmidi[0])
+                               snd_mpu401_uart_interrupt(irq,
+                                       ice->rmidi[0]->private_data);
+                       else /* disable RX to be sure */
+                               outb(inb(ICEREG1724(ice, IRQMASK)) |
+                                    VT1724_IRQ_MPU_RX,
+                                    ICEREG1724(ice, IRQMASK));
                }
+               /* ack MPU irq */
+               outb(status, ICEREG1724(ice, IRQSTAT));
                if (status & VT1724_IRQ_MTPCM) {
                        /*
                         * Multi-track PCM
@@ -2236,10 +2291,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
        }
 
        /* unmask used interrupts */
-       if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401))
-               mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
-       else
-               mask = 0;
+       mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
        outb(mask, ICEREG1724(ice, IRQMASK));
        /* don't handle FIFO overrun/underruns (just yet),
         * since they cause machine lockups
@@ -2373,14 +2425,29 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
 
        if (! c->no_mpu401) {
                if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
+                       struct snd_mpu401 *mpu;
                        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
                                                       ICEREG1724(ice, MPU_CTRL),
-                                                      MPU401_INFO_INTEGRATED,
+                                                      (MPU401_INFO_INTEGRATED |
+                                                       MPU401_INFO_TX_IRQ),
                                                       ice->irq, 0,
                                                       &ice->rmidi[0])) < 0) {
                                snd_card_free(card);
                                return err;
                        }
+                       mpu = ice->rmidi[0]->private_data;
+                       mpu->read = snd_vt1724_mpu401_read;
+                       mpu->write = snd_vt1724_mpu401_write;
+                       /* unmask MPU RX/TX irqs */
+                       outb(inb(ICEREG1724(ice, IRQMASK)) &
+                            ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
+                            ICEREG1724(ice, IRQMASK));
+#if 0 /* for testing */
+                       /* set watermarks */
+                       outb(VT1724_MPU_RX_FIFO | 0x1,
+                            ICEREG1724(ice, MPU_FIFO_WM));
+                       outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
+#endif
                }
        }
 
index 25ceb67..48d3679 100644 (file)
@@ -812,10 +812,6 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
                .build_controls = prodigy192_add_controls,
                .eeprom_size = sizeof(prodigy71_eeprom),
                .eeprom_data = prodigy71_eeprom,
-               /* the current MPU401 code loops infinitely
-                * when opening midi device
-                */
-               .no_mpu401 = 1,
        },
        { } /* terminator */
 };