~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/isa/gus/gus_uart.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 13 static 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 
 49 static 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 
 69 static 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 
 78 static 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 
 97 static 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 
130 static 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 
145 static 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 
160 static 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 
178 static 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 
216 static 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 
223 static 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 
230 int 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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php