treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 156
[sfrench/cifs-2.6.git] / sound / isa / gus / gus_uart.c
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, 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
48 static 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
68 static 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
77 static 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
96 static 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
129 static 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
144 static 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
159 static 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
177 static 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
215 static 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
222 static 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
229 int 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 }