xref: /linux/sound/firewire/fireworks/fireworks_midi.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * fireworks_midi.c - a part of driver for Fireworks based devices
4  *
5  * Copyright (c) 2009-2010 Clemens Ladisch
6  * Copyright (c) 2013-2014 Takashi Sakamoto
7  */
8 #include "fireworks.h"
9 
10 static int midi_open(struct snd_rawmidi_substream *substream)
11 {
12 	struct snd_efw *efw = substream->rmidi->private_data;
13 	int err;
14 
15 	err = snd_efw_stream_lock_try(efw);
16 	if (err < 0)
17 		return err;
18 
19 	scoped_guard(mutex, &efw->mutex) {
20 		err = snd_efw_stream_reserve_duplex(efw, 0, 0, 0);
21 		if (err >= 0) {
22 			++efw->substreams_counter;
23 			err = snd_efw_stream_start_duplex(efw);
24 			if (err < 0)
25 				--efw->substreams_counter;
26 		}
27 	}
28 	if (err < 0)
29 		snd_efw_stream_lock_release(efw);
30 	return err;
31 }
32 
33 static int midi_close(struct snd_rawmidi_substream *substream)
34 {
35 	struct snd_efw *efw = substream->rmidi->private_data;
36 
37 	scoped_guard(mutex, &efw->mutex) {
38 		--efw->substreams_counter;
39 		snd_efw_stream_stop_duplex(efw);
40 	}
41 
42 	snd_efw_stream_lock_release(efw);
43 	return 0;
44 }
45 
46 static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
47 {
48 	struct snd_efw *efw = substrm->rmidi->private_data;
49 
50 	guard(spinlock_irqsave)(&efw->lock);
51 
52 	if (up)
53 		amdtp_am824_midi_trigger(&efw->tx_stream,
54 					  substrm->number, substrm);
55 	else
56 		amdtp_am824_midi_trigger(&efw->tx_stream,
57 					  substrm->number, NULL);
58 }
59 
60 static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
61 {
62 	struct snd_efw *efw = substrm->rmidi->private_data;
63 
64 	guard(spinlock_irqsave)(&efw->lock);
65 
66 	if (up)
67 		amdtp_am824_midi_trigger(&efw->rx_stream,
68 					 substrm->number, substrm);
69 	else
70 		amdtp_am824_midi_trigger(&efw->rx_stream,
71 					 substrm->number, NULL);
72 }
73 
74 static void set_midi_substream_names(struct snd_efw *efw,
75 				     struct snd_rawmidi_str *str)
76 {
77 	struct snd_rawmidi_substream *subs;
78 
79 	list_for_each_entry(subs, &str->substreams, list) {
80 		scnprintf(subs->name, sizeof(subs->name),
81 			  "%s MIDI %d", efw->card->shortname, subs->number + 1);
82 	}
83 }
84 
85 int snd_efw_create_midi_devices(struct snd_efw *efw)
86 {
87 	static const struct snd_rawmidi_ops capture_ops = {
88 		.open		= midi_open,
89 		.close		= midi_close,
90 		.trigger	= midi_capture_trigger,
91 	};
92 	static const struct snd_rawmidi_ops playback_ops = {
93 		.open		= midi_open,
94 		.close		= midi_close,
95 		.trigger	= midi_playback_trigger,
96 	};
97 	struct snd_rawmidi *rmidi;
98 	struct snd_rawmidi_str *str;
99 	int err;
100 
101 	/* create midi ports */
102 	err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
103 			      efw->midi_out_ports, efw->midi_in_ports,
104 			      &rmidi);
105 	if (err < 0)
106 		return err;
107 
108 	snprintf(rmidi->name, sizeof(rmidi->name),
109 		 "%s MIDI", efw->card->shortname);
110 	rmidi->private_data = efw;
111 
112 	if (efw->midi_in_ports > 0) {
113 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
114 
115 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
116 				    &capture_ops);
117 
118 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
119 
120 		set_midi_substream_names(efw, str);
121 	}
122 
123 	if (efw->midi_out_ports > 0) {
124 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
125 
126 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
127 				    &playback_ops);
128 
129 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
130 
131 		set_midi_substream_names(efw, str);
132 	}
133 
134 	if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
135 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
136 
137 	return 0;
138 }
139