Merge tag 'thermal-v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/therma...
[linux-2.6-microblaze.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, 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 }