1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4*4882a593Smuzhiyun * Routines for control of SoundBlaster cards - MIDI interface
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * --
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
9*4882a593Smuzhiyun * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from
10*4882a593Smuzhiyun * working.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
13*4882a593Smuzhiyun * Added full duplex UART mode for DSP version 2.0 and later.
14*4882a593Smuzhiyun */
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/io.h>
17*4882a593Smuzhiyun #include <linux/time.h>
18*4882a593Smuzhiyun #include <sound/core.h>
19*4882a593Smuzhiyun #include <sound/sb.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun
snd_sb8dsp_midi_interrupt(struct snd_sb * chip)22*4882a593Smuzhiyun irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun struct snd_rawmidi *rmidi;
25*4882a593Smuzhiyun int max = 64;
26*4882a593Smuzhiyun char byte;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun if (!chip)
29*4882a593Smuzhiyun return IRQ_NONE;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun rmidi = chip->rmidi;
32*4882a593Smuzhiyun if (!rmidi) {
33*4882a593Smuzhiyun inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */
34*4882a593Smuzhiyun return IRQ_NONE;
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun spin_lock(&chip->midi_input_lock);
38*4882a593Smuzhiyun while (max-- > 0) {
39*4882a593Smuzhiyun if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
40*4882a593Smuzhiyun byte = inb(SBP(chip, READ));
41*4882a593Smuzhiyun if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
42*4882a593Smuzhiyun snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun spin_unlock(&chip->midi_input_lock);
47*4882a593Smuzhiyun return IRQ_HANDLED;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream * substream)50*4882a593Smuzhiyun static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun unsigned long flags;
53*4882a593Smuzhiyun struct snd_sb *chip;
54*4882a593Smuzhiyun unsigned int valid_open_flags;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun chip = substream->rmidi->private_data;
57*4882a593Smuzhiyun valid_open_flags = chip->hardware >= SB_HW_20
58*4882a593Smuzhiyun ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
59*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
60*4882a593Smuzhiyun if (chip->open & ~valid_open_flags) {
61*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
62*4882a593Smuzhiyun return -EAGAIN;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun chip->open |= SB_OPEN_MIDI_INPUT;
65*4882a593Smuzhiyun chip->midi_substream_input = substream;
66*4882a593Smuzhiyun if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
67*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
68*4882a593Smuzhiyun snd_sbdsp_reset(chip); /* reset DSP */
69*4882a593Smuzhiyun if (chip->hardware >= SB_HW_20)
70*4882a593Smuzhiyun snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
71*4882a593Smuzhiyun } else {
72*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun return 0;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream * substream)77*4882a593Smuzhiyun static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun unsigned long flags;
80*4882a593Smuzhiyun struct snd_sb *chip;
81*4882a593Smuzhiyun unsigned int valid_open_flags;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun chip = substream->rmidi->private_data;
84*4882a593Smuzhiyun valid_open_flags = chip->hardware >= SB_HW_20
85*4882a593Smuzhiyun ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
86*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
87*4882a593Smuzhiyun if (chip->open & ~valid_open_flags) {
88*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
89*4882a593Smuzhiyun return -EAGAIN;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun chip->open |= SB_OPEN_MIDI_OUTPUT;
92*4882a593Smuzhiyun chip->midi_substream_output = substream;
93*4882a593Smuzhiyun if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
94*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
95*4882a593Smuzhiyun snd_sbdsp_reset(chip); /* reset DSP */
96*4882a593Smuzhiyun if (chip->hardware >= SB_HW_20)
97*4882a593Smuzhiyun snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
98*4882a593Smuzhiyun } else {
99*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun return 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream * substream)104*4882a593Smuzhiyun static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun unsigned long flags;
107*4882a593Smuzhiyun struct snd_sb *chip;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun chip = substream->rmidi->private_data;
110*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
111*4882a593Smuzhiyun chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
112*4882a593Smuzhiyun chip->midi_substream_input = NULL;
113*4882a593Smuzhiyun if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
114*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
115*4882a593Smuzhiyun snd_sbdsp_reset(chip); /* reset DSP */
116*4882a593Smuzhiyun } else {
117*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream * substream)122*4882a593Smuzhiyun static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun unsigned long flags;
125*4882a593Smuzhiyun struct snd_sb *chip;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun chip = substream->rmidi->private_data;
128*4882a593Smuzhiyun del_timer_sync(&chip->midi_timer);
129*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
130*4882a593Smuzhiyun chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
131*4882a593Smuzhiyun chip->midi_substream_output = NULL;
132*4882a593Smuzhiyun if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
133*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
134*4882a593Smuzhiyun snd_sbdsp_reset(chip); /* reset DSP */
135*4882a593Smuzhiyun } else {
136*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream * substream,int up)141*4882a593Smuzhiyun static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun unsigned long flags;
144*4882a593Smuzhiyun struct snd_sb *chip;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun chip = substream->rmidi->private_data;
147*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
148*4882a593Smuzhiyun if (up) {
149*4882a593Smuzhiyun if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
150*4882a593Smuzhiyun if (chip->hardware < SB_HW_20)
151*4882a593Smuzhiyun snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
152*4882a593Smuzhiyun chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun } else {
155*4882a593Smuzhiyun if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
156*4882a593Smuzhiyun if (chip->hardware < SB_HW_20)
157*4882a593Smuzhiyun snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
158*4882a593Smuzhiyun chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun
snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream * substream)164*4882a593Smuzhiyun static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun unsigned long flags;
167*4882a593Smuzhiyun struct snd_sb *chip;
168*4882a593Smuzhiyun char byte;
169*4882a593Smuzhiyun int max = 32;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /* how big is Tx FIFO? */
172*4882a593Smuzhiyun chip = substream->rmidi->private_data;
173*4882a593Smuzhiyun while (max-- > 0) {
174*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
175*4882a593Smuzhiyun if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
176*4882a593Smuzhiyun chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
177*4882a593Smuzhiyun del_timer(&chip->midi_timer);
178*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
179*4882a593Smuzhiyun break;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun if (chip->hardware >= SB_HW_20) {
182*4882a593Smuzhiyun int timeout = 8;
183*4882a593Smuzhiyun while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
184*4882a593Smuzhiyun ;
185*4882a593Smuzhiyun if (timeout == 0) {
186*4882a593Smuzhiyun /* Tx FIFO full - try again later */
187*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
188*4882a593Smuzhiyun break;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun outb(byte, SBP(chip, WRITE));
191*4882a593Smuzhiyun } else {
192*4882a593Smuzhiyun snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
193*4882a593Smuzhiyun snd_sbdsp_command(chip, byte);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun snd_rawmidi_transmit_ack(substream, 1);
196*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun
snd_sb8dsp_midi_output_timer(struct timer_list * t)200*4882a593Smuzhiyun static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun struct snd_sb *chip = from_timer(chip, t, midi_timer);
203*4882a593Smuzhiyun struct snd_rawmidi_substream *substream = chip->midi_substream_output;
204*4882a593Smuzhiyun unsigned long flags;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
207*4882a593Smuzhiyun mod_timer(&chip->midi_timer, 1 + jiffies);
208*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
209*4882a593Smuzhiyun snd_sb8dsp_midi_output_write(substream);
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream * substream,int up)212*4882a593Smuzhiyun static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun unsigned long flags;
215*4882a593Smuzhiyun struct snd_sb *chip;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun chip = substream->rmidi->private_data;
218*4882a593Smuzhiyun spin_lock_irqsave(&chip->open_lock, flags);
219*4882a593Smuzhiyun if (up) {
220*4882a593Smuzhiyun if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
221*4882a593Smuzhiyun mod_timer(&chip->midi_timer, 1 + jiffies);
222*4882a593Smuzhiyun chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun } else {
225*4882a593Smuzhiyun if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
226*4882a593Smuzhiyun chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->open_lock, flags);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun if (up)
232*4882a593Smuzhiyun snd_sb8dsp_midi_output_write(substream);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun .open = snd_sb8dsp_midi_output_open,
238*4882a593Smuzhiyun .close = snd_sb8dsp_midi_output_close,
239*4882a593Smuzhiyun .trigger = snd_sb8dsp_midi_output_trigger,
240*4882a593Smuzhiyun };
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun .open = snd_sb8dsp_midi_input_open,
245*4882a593Smuzhiyun .close = snd_sb8dsp_midi_input_close,
246*4882a593Smuzhiyun .trigger = snd_sb8dsp_midi_input_trigger,
247*4882a593Smuzhiyun };
248*4882a593Smuzhiyun
snd_sb8dsp_midi(struct snd_sb * chip,int device)249*4882a593Smuzhiyun int snd_sb8dsp_midi(struct snd_sb *chip, int device)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun struct snd_rawmidi *rmidi;
252*4882a593Smuzhiyun int err;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
255*4882a593Smuzhiyun return err;
256*4882a593Smuzhiyun strcpy(rmidi->name, "SB8 MIDI");
257*4882a593Smuzhiyun snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
258*4882a593Smuzhiyun snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
259*4882a593Smuzhiyun rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
260*4882a593Smuzhiyun if (chip->hardware >= SB_HW_20)
261*4882a593Smuzhiyun rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
262*4882a593Smuzhiyun rmidi->private_data = chip;
263*4882a593Smuzhiyun timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
264*4882a593Smuzhiyun chip->rmidi = rmidi;
265*4882a593Smuzhiyun return 0;
266*4882a593Smuzhiyun }
267