linux/sound/isa/gus/gus_uart.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *  Routines for the GF1 MIDI interface - like UART 6850
   4 *
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 *
  20 */
  21
  22#include <linux/delay.h>
  23#include <linux/interrupt.h>
  24#include <linux/time.h>
  25#include <sound/core.h>
  26#include <sound/gus.h>
  27
  28static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
  29{
  30        int count;
  31        unsigned char stat, data, byte;
  32        unsigned long flags;
  33
  34        count = 10;
  35        while (count) {
  36                spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  37                stat = snd_gf1_uart_stat(gus);
  38                if (!(stat & 0x01)) {   /* data in Rx FIFO? */
  39                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  40                        count--;
  41                        continue;
  42                }
  43                count = 100;    /* arm counter to new value */
  44                data = snd_gf1_uart_get(gus);
  45                if (!(gus->gf1.uart_cmd & 0x80)) {
  46                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  47                        continue;
  48                }                       
  49                if (stat & 0x10) {      /* framing error */
  50                        gus->gf1.uart_framing++;
  51                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  52                        continue;
  53                }
  54                byte = snd_gf1_uart_get(gus);
  55                spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  56                snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
  57                if (stat & 0x20) {
  58                        gus->gf1.uart_overrun++;
  59                }
  60        }
  61}
  62
  63static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
  64{
  65        char byte;
  66        unsigned long flags;
  67
  68        /* try unlock output */
  69        if (snd_gf1_uart_stat(gus) & 0x01)
  70                snd_gf1_interrupt_midi_in(gus);
  71
  72        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  73        if (snd_gf1_uart_stat(gus) & 0x02) {    /* Tx FIFO free? */
  74                if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {  /* no other bytes or error */
  75                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
  76                } else {
  77                        snd_gf1_uart_put(gus, byte);
  78                }
  79        }
  80        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  81}
  82
  83static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
  84{
  85        snd_gf1_uart_cmd(gus, 0x03);    /* reset */
  86        if (!close && gus->uart_enable) {
  87                udelay(160);
  88                snd_gf1_uart_cmd(gus, 0x00);    /* normal operations */
  89        }
  90}
  91
  92static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
  93{
  94        unsigned long flags;
  95        struct snd_gus_card *gus;
  96
  97        gus = substream->rmidi->private_data;
  98        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  99        if (!(gus->gf1.uart_cmd & 0x80)) {      /* input active? */
 100                snd_gf1_uart_reset(gus, 0);
 101        }
 102        gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
 103        gus->midi_substream_output = substream;
 104        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 105#if 0
 106        snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
 107#endif
 108        return 0;
 109}
 110
 111static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
 112{
 113        unsigned long flags;
 114        struct snd_gus_card *gus;
 115        int i;
 116
 117        gus = substream->rmidi->private_data;
 118        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 119        if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
 120                snd_gf1_uart_reset(gus, 0);
 121        }
 122        gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
 123        gus->midi_substream_input = substream;
 124        if (gus->uart_enable) {
 125                for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
 126                        snd_gf1_uart_get(gus);  /* clean Rx */
 127                if (i >= 1000)
 128                        snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
 129        }
 130        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 131#if 0
 132        snd_printk(KERN_DEBUG
 133                   "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
 134                   gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
 135        snd_printk(KERN_DEBUG
 136                   "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x "
 137                   "(page = 0x%x)\n",
 138                   gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
 139                   inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
 140#endif
 141        return 0;
 142}
 143
 144static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
 145{
 146        unsigned long flags;
 147        struct snd_gus_card *gus;
 148
 149        gus = substream->rmidi->private_data;
 150        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 151        if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
 152                snd_gf1_uart_reset(gus, 1);
 153        snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
 154        gus->midi_substream_output = NULL;
 155        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 156        return 0;
 157}
 158
 159static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
 160{
 161        unsigned long flags;
 162        struct snd_gus_card *gus;
 163
 164        gus = substream->rmidi->private_data;
 165        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 166        if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
 167                snd_gf1_uart_reset(gus, 1);
 168        snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
 169        gus->midi_substream_input = NULL;
 170        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 171        return 0;
 172}
 173
 174static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
 175{
 176        struct snd_gus_card *gus;
 177        unsigned long flags;
 178
 179        gus = substream->rmidi->private_data;
 180
 181        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 182        if (up) {
 183                if ((gus->gf1.uart_cmd & 0x80) == 0)
 184                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
 185        } else {
 186                if (gus->gf1.uart_cmd & 0x80)
 187                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
 188        }
 189        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 190}
 191
 192static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
 193{
 194        unsigned long flags;
 195        struct snd_gus_card *gus;
 196        char byte;
 197        int timeout;
 198
 199        gus = substream->rmidi->private_data;
 200
 201        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 202        if (up) {
 203                if ((gus->gf1.uart_cmd & 0x20) == 0) {
 204                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 205                        /* wait for empty Rx - Tx is probably unlocked */
 206                        timeout = 10000;
 207                        while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
 208                        /* Tx FIFO free? */
 209                        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 210                        if (gus->gf1.uart_cmd & 0x20) {
 211                                spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 212                                return;
 213                        }
 214                        if (snd_gf1_uart_stat(gus) & 0x02) {
 215                                if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
 216                                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 217                                        return;
 218                                }
 219                                snd_gf1_uart_put(gus, byte);
 220                        }
 221                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);        /* enable Tx interrupt */
 222                }
 223        } else {
 224                if (gus->gf1.uart_cmd & 0x20)
 225                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
 226        }
 227        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 228}
 229
 230static struct snd_rawmidi_ops snd_gf1_uart_output =
 231{
 232        .open =         snd_gf1_uart_output_open,
 233        .close =        snd_gf1_uart_output_close,
 234        .trigger =      snd_gf1_uart_output_trigger,
 235};
 236
 237static struct snd_rawmidi_ops snd_gf1_uart_input =
 238{
 239        .open =         snd_gf1_uart_input_open,
 240        .close =        snd_gf1_uart_input_close,
 241        .trigger =      snd_gf1_uart_input_trigger,
 242};
 243
 244int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi)
 245{
 246        struct snd_rawmidi *rmidi;
 247        int err;
 248
 249        if (rrawmidi)
 250                *rrawmidi = NULL;
 251        if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
 252                return err;
 253        strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
 254        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
 255        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
 256        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
 257        rmidi->private_data = gus;
 258        gus->midi_uart = rmidi;
 259        if (rrawmidi)
 260                *rrawmidi = rmidi;
 261        return err;
 262}
 263