treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 156
[linux-block.git] / sound / isa / gus / gus_uart.c
CommitLineData
1a59d1b8 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4 2/*
c1017a4c 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4 4 * Routines for the GF1 MIDI interface - like UART 6850
1da177e4
LT
5 */
6
1da177e4
LT
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
5e2da206 13static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
1da177e4
LT
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
5e2da206 48static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
1da177e4
LT
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
5e2da206 68static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
1da177e4
LT
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
5e2da206 77static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
1da177e4
LT
78{
79 unsigned long flags;
5e2da206 80 struct snd_gus_card *gus;
1da177e4
LT
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
99b359ba 91 snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
1da177e4
LT
92#endif
93 return 0;
94}
95
5e2da206 96static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
1da177e4
LT
97{
98 unsigned long flags;
5e2da206 99 struct snd_gus_card *gus;
1da177e4
LT
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)
99b359ba 113 snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
1da177e4
LT
114 }
115 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
116#if 0
91f05060
TI
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));
1da177e4
LT
125#endif
126 return 0;
127}
128
5e2da206 129static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
1da177e4
LT
130{
131 unsigned long flags;
5e2da206 132 struct snd_gus_card *gus;
1da177e4
LT
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
5e2da206 144static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
1da177e4
LT
145{
146 unsigned long flags;
5e2da206 147 struct snd_gus_card *gus;
1da177e4
LT
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
5e2da206 159static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
1da177e4 160{
5e2da206 161 struct snd_gus_card *gus;
1da177e4
LT
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
5e2da206 177static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
1da177e4
LT
178{
179 unsigned long flags;
5e2da206 180 struct snd_gus_card *gus;
1da177e4
LT
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
9021b2b8 215static const struct snd_rawmidi_ops snd_gf1_uart_output =
1da177e4
LT
216{
217 .open = snd_gf1_uart_output_open,
218 .close = snd_gf1_uart_output_close,
219 .trigger = snd_gf1_uart_output_trigger,
220};
221
9021b2b8 222static const struct snd_rawmidi_ops snd_gf1_uart_input =
1da177e4
LT
223{
224 .open = snd_gf1_uart_input_open,
225 .close = snd_gf1_uart_input_close,
226 .trigger = snd_gf1_uart_input_trigger,
227};
228
db5abb3c 229int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
1da177e4 230{
5e2da206 231 struct snd_rawmidi *rmidi;
1da177e4
LT
232 int err;
233
1da177e4
LT
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;
1da177e4
LT
242 return err;
243}