Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/isa/gus/gus_uart.c
10817 views
1
/*
2
* Copyright (c) by Jaroslav Kysela <[email protected]>
3
* Routines for the GF1 MIDI interface - like UART 6850
4
*
5
*
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
10
*
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
*
20
*/
21
22
#include <linux/delay.h>
23
#include <linux/interrupt.h>
24
#include <linux/time.h>
25
#include <sound/core.h>
26
#include <sound/gus.h>
27
28
static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
29
{
30
int count;
31
unsigned char stat, data, byte;
32
unsigned long flags;
33
34
count = 10;
35
while (count) {
36
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
37
stat = snd_gf1_uart_stat(gus);
38
if (!(stat & 0x01)) { /* data in Rx FIFO? */
39
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
40
count--;
41
continue;
42
}
43
count = 100; /* arm counter to new value */
44
data = snd_gf1_uart_get(gus);
45
if (!(gus->gf1.uart_cmd & 0x80)) {
46
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
47
continue;
48
}
49
if (stat & 0x10) { /* framing error */
50
gus->gf1.uart_framing++;
51
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
52
continue;
53
}
54
byte = snd_gf1_uart_get(gus);
55
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
56
snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
57
if (stat & 0x20) {
58
gus->gf1.uart_overrun++;
59
}
60
}
61
}
62
63
static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
64
{
65
char byte;
66
unsigned long flags;
67
68
/* try unlock output */
69
if (snd_gf1_uart_stat(gus) & 0x01)
70
snd_gf1_interrupt_midi_in(gus);
71
72
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
73
if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */
74
if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */
75
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
76
} else {
77
snd_gf1_uart_put(gus, byte);
78
}
79
}
80
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
81
}
82
83
static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
84
{
85
snd_gf1_uart_cmd(gus, 0x03); /* reset */
86
if (!close && gus->uart_enable) {
87
udelay(160);
88
snd_gf1_uart_cmd(gus, 0x00); /* normal operations */
89
}
90
}
91
92
static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
93
{
94
unsigned long flags;
95
struct snd_gus_card *gus;
96
97
gus = substream->rmidi->private_data;
98
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
99
if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */
100
snd_gf1_uart_reset(gus, 0);
101
}
102
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
103
gus->midi_substream_output = substream;
104
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
105
#if 0
106
snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
107
#endif
108
return 0;
109
}
110
111
static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
112
{
113
unsigned long flags;
114
struct snd_gus_card *gus;
115
int i;
116
117
gus = substream->rmidi->private_data;
118
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
119
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
120
snd_gf1_uart_reset(gus, 0);
121
}
122
gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
123
gus->midi_substream_input = substream;
124
if (gus->uart_enable) {
125
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
126
snd_gf1_uart_get(gus); /* clean Rx */
127
if (i >= 1000)
128
snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
129
}
130
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
131
#if 0
132
snd_printk(KERN_DEBUG
133
"read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
134
gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
135
snd_printk(KERN_DEBUG
136
"[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x "
137
"(page = 0x%x)\n",
138
gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
139
inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
140
#endif
141
return 0;
142
}
143
144
static int snd_gf1_uart_output_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_in != snd_gf1_interrupt_midi_in)
152
snd_gf1_uart_reset(gus, 1);
153
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
154
gus->midi_substream_output = NULL;
155
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
156
return 0;
157
}
158
159
static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
160
{
161
unsigned long flags;
162
struct snd_gus_card *gus;
163
164
gus = substream->rmidi->private_data;
165
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
166
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
167
snd_gf1_uart_reset(gus, 1);
168
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
169
gus->midi_substream_input = NULL;
170
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
171
return 0;
172
}
173
174
static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
175
{
176
struct snd_gus_card *gus;
177
unsigned long flags;
178
179
gus = substream->rmidi->private_data;
180
181
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
182
if (up) {
183
if ((gus->gf1.uart_cmd & 0x80) == 0)
184
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
185
} else {
186
if (gus->gf1.uart_cmd & 0x80)
187
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
188
}
189
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
190
}
191
192
static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
193
{
194
unsigned long flags;
195
struct snd_gus_card *gus;
196
char byte;
197
int timeout;
198
199
gus = substream->rmidi->private_data;
200
201
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
202
if (up) {
203
if ((gus->gf1.uart_cmd & 0x20) == 0) {
204
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
205
/* wait for empty Rx - Tx is probably unlocked */
206
timeout = 10000;
207
while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
208
/* Tx FIFO free? */
209
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
210
if (gus->gf1.uart_cmd & 0x20) {
211
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
212
return;
213
}
214
if (snd_gf1_uart_stat(gus) & 0x02) {
215
if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
216
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
217
return;
218
}
219
snd_gf1_uart_put(gus, byte);
220
}
221
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */
222
}
223
} else {
224
if (gus->gf1.uart_cmd & 0x20)
225
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
226
}
227
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
228
}
229
230
static struct snd_rawmidi_ops snd_gf1_uart_output =
231
{
232
.open = snd_gf1_uart_output_open,
233
.close = snd_gf1_uart_output_close,
234
.trigger = snd_gf1_uart_output_trigger,
235
};
236
237
static struct snd_rawmidi_ops snd_gf1_uart_input =
238
{
239
.open = snd_gf1_uart_input_open,
240
.close = snd_gf1_uart_input_close,
241
.trigger = snd_gf1_uart_input_trigger,
242
};
243
244
int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi)
245
{
246
struct snd_rawmidi *rmidi;
247
int err;
248
249
if (rrawmidi)
250
*rrawmidi = NULL;
251
if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
252
return err;
253
strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
254
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
255
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
256
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
257
rmidi->private_data = gus;
258
gus->midi_uart = rmidi;
259
if (rrawmidi)
260
*rrawmidi = rmidi;
261
return err;
262
}
263
264