Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / sound / drivers / mpu401 / mpu401_uart.c
index b49a45c..4bf07ca 100644 (file)
@@ -58,22 +58,26 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu);
 #define MPU401_ACK             0xfe
 
 /* Build in lowlevel io */
-static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr)
+static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,
+                             unsigned long addr)
 {
        outb(data, addr);
 }
 
-static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, unsigned long addr)
+static unsigned char mpu401_read_port(struct snd_mpu401 *mpu,
+                                     unsigned long addr)
 {
        return inb(addr);
 }
 
-static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr)
+static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data,
+                             unsigned long addr)
 {
        writeb(data, (void __iomem *)addr);
 }
 
-static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, unsigned long addr)
+static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu,
+                                     unsigned long addr)
 {
        return readb((void __iomem *)addr);
 }
@@ -86,20 +90,13 @@ static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)
                mpu->read(mpu, MPU401D(mpu));
 #ifdef CONFIG_SND_DEBUG
        if (timeout <= 0)
-               snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
+               snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n",
+                          mpu->read(mpu, MPU401C(mpu)));
 #endif
 }
 
-static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
+static void uart_interrupt_tx(struct snd_mpu401 *mpu)
 {
-       spin_lock(&mpu->input_lock);
-       if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
-               snd_mpu401_uart_input_read(mpu);
-       } else {
-               snd_mpu401_uart_clear_rx(mpu);
-       }
-       spin_unlock(&mpu->input_lock);
-       /* ok. for better Tx performance try do some output when input is done */
        if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
            test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
                spin_lock(&mpu->output_lock);
@@ -108,6 +105,22 @@ static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
        }
 }
 
+static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
+{
+       if (mpu->info_flags & MPU401_INFO_INPUT) {
+               spin_lock(&mpu->input_lock);
+               if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
+                       snd_mpu401_uart_input_read(mpu);
+               else
+                       snd_mpu401_uart_clear_rx(mpu);
+               spin_unlock(&mpu->input_lock);
+       }
+       if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+               /* ok. for better Tx performance try do some output
+                  when input is done */
+               uart_interrupt_tx(mpu);
+}
+
 /**
  * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler
  * @irq: the irq number
@@ -116,7 +129,8 @@ static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
  *
  * Processes the interrupt for MPU401-UART i/o.
  */
-irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id,
+                                     struct pt_regs *regs)
 {
        struct snd_mpu401 *mpu = dev_id;
        
@@ -126,6 +140,29 @@ irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *reg
        return IRQ_HANDLED;
 }
 
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
+
+/**
+ * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler
+ * @irq: the irq number
+ * @dev_id: mpu401 instance
+ * @regs: the reigster
+ *
+ * Processes the interrupt for MPU401-UART output.
+ */
+irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id,
+                                        struct pt_regs *regs)
+{
+       struct snd_mpu401 *mpu = dev_id;
+       
+       if (mpu == NULL)
+               return IRQ_NONE;
+       uart_interrupt_tx(mpu);
+       return IRQ_HANDLED;
+}
+
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
+
 /*
  * timer callback
  * reprogram the timer and call the interrupt job
@@ -159,7 +196,8 @@ static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
                mpu->timer.expires = 1 + jiffies;
                add_timer(&mpu->timer);
        } 
-       mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER;
+       mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :
+               MPU401_MODE_OUTPUT_TIMER;
        spin_unlock_irqrestore (&mpu->timer_lock, flags);
 }
 
@@ -172,7 +210,8 @@ static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
 
        spin_lock_irqsave (&mpu->timer_lock, flags);
        if (mpu->timer_invoked) {
-               mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER;
+               mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :
+                       ~MPU401_MODE_OUTPUT_TIMER;
                if (! mpu->timer_invoked)
                        del_timer(&mpu->timer);
        }
@@ -180,11 +219,12 @@ static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
 }
 
 /*
-
+ * send a UART command
+ * return zero if successful, non-zero for some errors
  */
 
 static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
-               int ack)
+                              int ack)
 {
        unsigned long flags;
        int timeout, ok;
@@ -196,11 +236,13 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
        }
        /* ok. standard MPU-401 initialization */
        if (mpu->hardware != MPU401_HW_SB) {
-               for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--)
+               for (timeout = 1000; timeout > 0 &&
+                            !snd_mpu401_output_ready(mpu); timeout--)
                        udelay(10);
 #ifdef CONFIG_SND_DEBUG
                if (!timeout)
-                       snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
+                       snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n",
+                                  mpu->read(mpu, MPU401C(mpu)));
 #endif
        }
        mpu->write(mpu, cmd, MPU401C(mpu));
@@ -215,12 +257,14 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
                }
                if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
                        ok = 1;
-       } else {
+       } else
                ok = 1;
-       }
        spin_unlock_irqrestore(&mpu->input_lock, flags);
        if (!ok) {
-               snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu)));
+               snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx "
+                          "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port,
+                          mpu->read(mpu, MPU401C(mpu)),
+                          mpu->read(mpu, MPU401D(mpu)));
                return 1;
        }
        return 0;
@@ -314,7 +358,8 @@ static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)
 /*
  * trigger input callback
  */
-static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
+static void
+snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
 {
        unsigned long flags;
        struct snd_mpu401 *mpu;
@@ -322,7 +367,8 @@ static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substrea
 
        mpu = substream->rmidi->private_data;
        if (up) {
-               if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) {
+               if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER,
+                                      &mpu->mode)) {
                        /* first time - flush FIFO */
                        while (max-- > 0)
                                mpu->read(mpu, MPU401D(mpu));
@@ -352,13 +398,11 @@ static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)
        unsigned char byte;
 
        while (max-- > 0) {
-               if (snd_mpu401_input_avail(mpu)) {
-                       byte = mpu->read(mpu, MPU401D(mpu));
-                       if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
-                               snd_rawmidi_receive(mpu->substream_input, &byte, 1);
-               } else {
+               if (! snd_mpu401_input_avail(mpu))
                        break; /* input not available */
-               }
+               byte = mpu->read(mpu, MPU401D(mpu));
+               if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
+                       snd_rawmidi_receive(mpu->substream_input, &byte, 1);
        }
 }
 
@@ -380,16 +424,16 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
        int max = 256, timeout;
 
        do {
-               if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) {
+               if (snd_rawmidi_transmit_peek(mpu->substream_output,
+                                             &byte, 1) == 1) {
                        for (timeout = 100; timeout > 0; timeout--) {
-                               if (snd_mpu401_output_ready(mpu)) {
-                                       mpu->write(mpu, byte, MPU401D(mpu));
-                                       snd_rawmidi_transmit_ack(mpu->substream_output, 1);
+                               if (snd_mpu401_output_ready(mpu))
                                        break;
-                               }
                        }
                        if (timeout == 0)
                                break;  /* Tx FIFO full - try again later */
+                       mpu->write(mpu, byte, MPU401D(mpu));
+                       snd_rawmidi_transmit_ack(mpu->substream_output, 1);
                } else {
                        snd_mpu401_uart_remove_timer (mpu, 0);
                        break;  /* no other data - leave the tx loop */
@@ -400,7 +444,8 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
 /*
  * output trigger callback
  */
-static void snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
+static void
+snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
 {
        unsigned long flags;
        struct snd_mpu401 *mpu;
@@ -413,14 +458,16 @@ static void snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substre
                 * since the output timer might have been removed in
                 * snd_mpu401_uart_output_write().
                 */
-               snd_mpu401_uart_add_timer(mpu, 0);
+               if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+                       snd_mpu401_uart_add_timer(mpu, 0);
 
                /* output pending data */
                spin_lock_irqsave(&mpu->output_lock, flags);
                snd_mpu401_uart_output_write(mpu);
                spin_unlock_irqrestore(&mpu->output_lock, flags);
        } else {
-               snd_mpu401_uart_remove_timer(mpu, 0);
+               if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+                       snd_mpu401_uart_remove_timer(mpu, 0);
                clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
        }
 }
@@ -458,7 +505,7 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
  * @device: the device index, zero-based
  * @hardware: the hardware type, MPU401_HW_XXXX
  * @port: the base address of MPU401 port
- * @integrated: non-zero if the port was already reserved by the chip
+ * @info_flags: bitflags MPU401_INFO_XXX
  * @irq: the irq number, -1 if no interrupt for mpu
  * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved.
  * @rrawmidi: the pointer to store the new rawmidi instance
@@ -473,17 +520,24 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
  */
 int snd_mpu401_uart_new(struct snd_card *card, int device,
                        unsigned short hardware,
-                       unsigned long port, int integrated,
+                       unsigned long port,
+                       unsigned int info_flags,
                        int irq, int irq_flags,
                        struct snd_rawmidi ** rrawmidi)
 {
        struct snd_mpu401 *mpu;
        struct snd_rawmidi *rmidi;
+       int in_enable, out_enable;
        int err;
 
        if (rrawmidi)
                *rrawmidi = NULL;
-       if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0)
+       if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT)))
+               info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
+       in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
+       out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
+       if ((err = snd_rawmidi_new(card, "MPU-401U", device,
+                                  out_enable, in_enable, &rmidi)) < 0)
                return err;
        mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
        if (mpu == NULL) {
@@ -497,23 +551,23 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
        spin_lock_init(&mpu->output_lock);
        spin_lock_init(&mpu->timer_lock);
        mpu->hardware = hardware;
-       if (!integrated) {
+       if (! (info_flags & MPU401_INFO_INTEGRATED)) {
                int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
-               if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) {
-                       snd_printk(KERN_ERR "mpu401_uart: unable to grab port 0x%lx size %d\n", port, res_size);
+               mpu->res = request_region(port, res_size, "MPU401 UART");
+               if (mpu->res == NULL) {
+                       snd_printk(KERN_ERR "mpu401_uart: "
+                                  "unable to grab port 0x%lx size %d\n",
+                                  port, res_size);
                        snd_device_free(card, rmidi);
                        return -EBUSY;
                }
        }
-       switch (hardware) {
-       case MPU401_HW_AUREAL:
+       if (info_flags & MPU401_INFO_MMIO) {
                mpu->write = mpu401_write_mmio;
                mpu->read = mpu401_read_mmio;
-               break;
-       default:
+       } else {
                mpu->write = mpu401_write_port;
                mpu->read = mpu401_read_port;
-               break;
        }
        mpu->port = port;
        if (hardware == MPU401_HW_PC98II)
@@ -521,30 +575,40 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
        else
                mpu->cport = port + 1;
        if (irq >= 0 && irq_flags) {
-               if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) {
-                       snd_printk(KERN_ERR "mpu401_uart: unable to grab IRQ %d\n", irq);
+               if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags,
+                               "MPU401 UART", (void *) mpu)) {
+                       snd_printk(KERN_ERR "mpu401_uart: "
+                                  "unable to grab IRQ %d\n", irq);
                        snd_device_free(card, rmidi);
                        return -EBUSY;
                }
        }
+       mpu->info_flags = info_flags;
        mpu->irq = irq;
        mpu->irq_flags = irq_flags;
        if (card->shortname[0])
-               snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname);
+               snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",
+                        card->shortname);
        else
-               sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device);
-       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output);
-       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input);
-       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
-                            SNDRV_RAWMIDI_INFO_INPUT |
-                            SNDRV_RAWMIDI_INFO_DUPLEX;
+               sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device);
+       if (out_enable) {
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                   &snd_mpu401_uart_output);
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+       }
+       if (in_enable) {
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                                   &snd_mpu401_uart_input);
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+               if (out_enable)
+                       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+       }
        mpu->rmidi = rmidi;
        if (rrawmidi)
                *rrawmidi = rmidi;
        return 0;
 }
 
-EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
 EXPORT_SYMBOL(snd_mpu401_uart_new);
 
 /*