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