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