]> err.no Git - linux-2.6/blobdiff - sound/pci/ice1712/ice1724.c
Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
[linux-2.6] / sound / pci / ice1712 / ice1724.c
index 13ea94f317cb816a817bfe4e80e2ea11a1026762..e596d777d9dde630d12532c55a578937f8d5b2ee 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/info.h>
-#include <sound/mpu401.h>
+#include <sound/rawmidi.h>
 #include <sound/initval.h>
 
 #include <sound/asoundef.h>
@@ -222,6 +222,155 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
        return data;
 }
 
+/*
+ * MIDI
+ */
+
+static void vt1724_midi_clear_rx(struct snd_ice1712 *ice)
+{
+       unsigned int count;
+
+       for (count = inb(ICEREG1724(ice, MPU_RXFIFO)); count > 0; --count)
+               inb(ICEREG1724(ice, MPU_DATA));
+}
+
+static inline struct snd_rawmidi_substream *
+get_rawmidi_substream(struct snd_ice1712 *ice, unsigned int stream)
+{
+       return list_first_entry(&ice->rmidi[0]->streams[stream].substreams,
+                               struct snd_rawmidi_substream, list);
+}
+
+static void vt1724_midi_write(struct snd_ice1712 *ice)
+{
+       struct snd_rawmidi_substream *s;
+       int count, i;
+       u8 buffer[32];
+
+       s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_OUTPUT);
+       count = 31 - inb(ICEREG1724(ice, MPU_TXFIFO));
+       if (count > 0) {
+               count = snd_rawmidi_transmit(s, buffer, count);
+               for (i = 0; i < count; ++i)
+                       outb(buffer[i], ICEREG1724(ice, MPU_DATA));
+       }
+}
+
+static void vt1724_midi_read(struct snd_ice1712 *ice)
+{
+       struct snd_rawmidi_substream *s;
+       int count, i;
+       u8 buffer[32];
+
+       s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_INPUT);
+       count = inb(ICEREG1724(ice, MPU_RXFIFO));
+       if (count > 0) {
+               count = min(count, 32);
+               for (i = 0; i < count; ++i)
+                       buffer[i] = inb(ICEREG1724(ice, MPU_DATA));
+               snd_rawmidi_receive(s, buffer, count);
+       }
+}
+
+static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
+                                  u8 flag, int enable)
+{
+       struct snd_ice1712 *ice = substream->rmidi->private_data;
+       u8 mask;
+
+       spin_lock_irq(&ice->reg_lock);
+       mask = inb(ICEREG1724(ice, IRQMASK));
+       if (enable)
+               mask &= ~flag;
+       else
+               mask |= flag;
+       outb(mask, ICEREG1724(ice, IRQMASK));
+       spin_unlock_irq(&ice->reg_lock);
+}
+
+static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
+{
+       vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 1);
+       return 0;
+}
+
+static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
+{
+       vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
+       return 0;
+}
+
+static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
+{
+       struct snd_ice1712 *ice = s->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ice->reg_lock, flags);
+       if (up) {
+               ice->midi_output = 1;
+               vt1724_midi_write(ice);
+       } else {
+               ice->midi_output = 0;
+       }
+       spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
+{
+       struct snd_ice1712 *ice = s->rmidi->private_data;
+       unsigned long timeout;
+
+       /* 32 bytes should be transmitted in less than about 12 ms */
+       timeout = jiffies + msecs_to_jiffies(15);
+       do {
+               if (inb(ICEREG1724(ice, MPU_CTRL)) & VT1724_MPU_TX_EMPTY)
+                       break;
+               schedule_timeout_uninterruptible(1);
+       } while (time_after(timeout, jiffies));
+}
+
+static struct snd_rawmidi_ops vt1724_midi_output_ops = {
+       .open = vt1724_midi_output_open,
+       .close = vt1724_midi_output_close,
+       .trigger = vt1724_midi_output_trigger,
+       .drain = vt1724_midi_output_drain,
+};
+
+static int vt1724_midi_input_open(struct snd_rawmidi_substream *s)
+{
+       vt1724_midi_clear_rx(s->rmidi->private_data);
+       vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 1);
+       return 0;
+}
+
+static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
+{
+       vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 0);
+       return 0;
+}
+
+static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
+{
+       struct snd_ice1712 *ice = s->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ice->reg_lock, flags);
+       if (up) {
+               ice->midi_input = 1;
+               vt1724_midi_read(ice);
+       } else {
+               ice->midi_input = 0;
+       }
+       spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops vt1724_midi_input_ops = {
+       .open = vt1724_midi_input_open,
+       .close = vt1724_midi_input_close,
+       .trigger = vt1724_midi_input_trigger,
+};
+
+
 /*
  *  Interrupt handler
  */
@@ -230,24 +379,49 @@ 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 (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);
+               if (status & VT1724_IRQ_MPU_TX) {
+                       spin_lock(&ice->reg_lock);
+                       if (ice->midi_output)
+                               vt1724_midi_write(ice);
+                       spin_unlock(&ice->reg_lock);
+                       /* 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) {
+                       spin_lock(&ice->reg_lock);
+                       if (ice->midi_input)
+                               vt1724_midi_read(ice);
+                       else
+                               vt1724_midi_clear_rx(ice);
+                       spin_unlock(&ice->reg_lock);
                }
+               /* ack MPU irq */
+               outb(status, ICEREG1724(ice, IRQSTAT));
                if (status & VT1724_IRQ_MTPCM) {
                        /*
                         * Multi-track PCM
@@ -2236,10 +2410,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 +2544,30 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
 
        if (! c->no_mpu401) {
                if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
-                       if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
-                                                      ICEREG1724(ice, MPU_CTRL),
-                                                      MPU401_INFO_INTEGRATED,
-                                                      ice->irq, 0,
-                                                      &ice->rmidi[0])) < 0) {
+                       struct snd_rawmidi *rmidi;
+
+                       err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
+                       if (err < 0) {
                                snd_card_free(card);
                                return err;
                        }
+                       ice->rmidi[0] = rmidi;
+                       rmidi->private_data = ice;
+                       strcpy(rmidi->name, "ICE1724 MIDI");
+                       rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+                                           SNDRV_RAWMIDI_INFO_INPUT |
+                                           SNDRV_RAWMIDI_INFO_DUPLEX;
+                       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                           &vt1724_midi_output_ops);
+                       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                                           &vt1724_midi_input_ops);
+
+                       /* set watermarks */
+                       outb(VT1724_MPU_RX_FIFO | 0x1,
+                            ICEREG1724(ice, MPU_FIFO_WM));
+                       outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
+                       /* set UART mode */
+                       outb(VT1724_MPU_UART, ICEREG1724(ice, MPU_CTRL));
                }
        }