Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/isa/gus/gus_uart.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (c) by Jaroslav Kysela <[email protected]>
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
53
/* try unlock output */
54
if (snd_gf1_uart_stat(gus) & 0x01)
55
snd_gf1_interrupt_midi_in(gus);
56
57
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
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
}
66
67
static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
68
{
69
snd_gf1_uart_cmd(gus, 0x03); /* reset */
70
if (!close && gus->uart_enable) {
71
udelay(160);
72
snd_gf1_uart_cmd(gus, 0x00); /* normal operations */
73
}
74
}
75
76
static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
77
{
78
struct snd_gus_card *gus;
79
80
gus = substream->rmidi->private_data;
81
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
82
if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */
83
snd_gf1_uart_reset(gus, 0);
84
}
85
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
86
gus->midi_substream_output = substream;
87
#if 0
88
dev_dbg(gus->card->dev,
89
"write init - cmd = 0x%x, stat = 0x%x\n",
90
gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
91
#endif
92
return 0;
93
}
94
95
static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
96
{
97
struct snd_gus_card *gus;
98
int i;
99
100
gus = substream->rmidi->private_data;
101
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
102
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
103
snd_gf1_uart_reset(gus, 0);
104
}
105
gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
106
gus->midi_substream_input = substream;
107
if (gus->uart_enable) {
108
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
109
snd_gf1_uart_get(gus); /* clean Rx */
110
if (i >= 1000)
111
dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");
112
}
113
#if 0
114
dev_dbg(gus->card->dev,
115
"read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
116
gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
117
dev_dbg(gus->card->dev,
118
"[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",
119
gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
120
inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
121
#endif
122
return 0;
123
}
124
125
static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
126
{
127
struct snd_gus_card *gus;
128
129
gus = substream->rmidi->private_data;
130
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
131
if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
132
snd_gf1_uart_reset(gus, 1);
133
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
134
gus->midi_substream_output = NULL;
135
return 0;
136
}
137
138
static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
139
{
140
struct snd_gus_card *gus;
141
142
gus = substream->rmidi->private_data;
143
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
144
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
145
snd_gf1_uart_reset(gus, 1);
146
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
147
gus->midi_substream_input = NULL;
148
return 0;
149
}
150
151
static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
152
{
153
struct snd_gus_card *gus;
154
155
gus = substream->rmidi->private_data;
156
157
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
158
if (up) {
159
if ((gus->gf1.uart_cmd & 0x80) == 0)
160
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
161
} else {
162
if (gus->gf1.uart_cmd & 0x80)
163
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
164
}
165
}
166
167
static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
168
{
169
unsigned long flags;
170
struct snd_gus_card *gus;
171
char byte;
172
int timeout;
173
174
gus = substream->rmidi->private_data;
175
176
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
177
if (up) {
178
if ((gus->gf1.uart_cmd & 0x20) == 0) {
179
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
180
/* wait for empty Rx - Tx is probably unlocked */
181
timeout = 10000;
182
while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
183
/* Tx FIFO free? */
184
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
185
if (gus->gf1.uart_cmd & 0x20) {
186
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
187
return;
188
}
189
if (snd_gf1_uart_stat(gus) & 0x02) {
190
if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
191
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
192
return;
193
}
194
snd_gf1_uart_put(gus, byte);
195
}
196
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */
197
}
198
} else {
199
if (gus->gf1.uart_cmd & 0x20)
200
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
201
}
202
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
203
}
204
205
static const struct snd_rawmidi_ops snd_gf1_uart_output =
206
{
207
.open = snd_gf1_uart_output_open,
208
.close = snd_gf1_uart_output_close,
209
.trigger = snd_gf1_uart_output_trigger,
210
};
211
212
static const struct snd_rawmidi_ops snd_gf1_uart_input =
213
{
214
.open = snd_gf1_uart_input_open,
215
.close = snd_gf1_uart_input_close,
216
.trigger = snd_gf1_uart_input_trigger,
217
};
218
219
int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
220
{
221
struct snd_rawmidi *rmidi;
222
int err;
223
224
err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
225
if (err < 0)
226
return err;
227
strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
228
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
229
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
230
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
231
rmidi->private_data = gus;
232
gus->midi_uart = rmidi;
233
return err;
234
}
235
236