xref: /linux/sound/firewire/oxfw/oxfw-midi.c (revision 55a42f78ffd386e01a5404419f8c5ded7db70a21)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * oxfw_midi.c - a part of driver for OXFW970/971 based devices
4  *
5  * Copyright (c) 2014 Takashi Sakamoto
6  */
7 
8 #include "oxfw.h"
9 
10 static int midi_capture_open(struct snd_rawmidi_substream *substream)
11 {
12 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
13 	int err;
14 
15 	err = snd_oxfw_stream_lock_try(oxfw);
16 	if (err < 0)
17 		return err;
18 
19 	scoped_guard(mutex, &oxfw->mutex) {
20 		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0);
21 		if (err >= 0) {
22 			++oxfw->substreams_count;
23 			err = snd_oxfw_stream_start_duplex(oxfw);
24 			if (err < 0)
25 				--oxfw->substreams_count;
26 		}
27 	}
28 
29 	if (err < 0)
30 		snd_oxfw_stream_lock_release(oxfw);
31 
32 	return err;
33 }
34 
35 static int midi_playback_open(struct snd_rawmidi_substream *substream)
36 {
37 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
38 	int err;
39 
40 	err = snd_oxfw_stream_lock_try(oxfw);
41 	if (err < 0)
42 		return err;
43 
44 	scoped_guard(mutex, &oxfw->mutex) {
45 		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0);
46 		if (err >= 0) {
47 			++oxfw->substreams_count;
48 			err = snd_oxfw_stream_start_duplex(oxfw);
49 		}
50 	}
51 
52 	if (err < 0)
53 		snd_oxfw_stream_lock_release(oxfw);
54 
55 	return err;
56 }
57 
58 static int midi_capture_close(struct snd_rawmidi_substream *substream)
59 {
60 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
61 
62 	scoped_guard(mutex, &oxfw->mutex) {
63 		--oxfw->substreams_count;
64 		snd_oxfw_stream_stop_duplex(oxfw);
65 	}
66 
67 	snd_oxfw_stream_lock_release(oxfw);
68 	return 0;
69 }
70 
71 static int midi_playback_close(struct snd_rawmidi_substream *substream)
72 {
73 	struct snd_oxfw *oxfw = substream->rmidi->private_data;
74 
75 	scoped_guard(mutex, &oxfw->mutex) {
76 		--oxfw->substreams_count;
77 		snd_oxfw_stream_stop_duplex(oxfw);
78 	}
79 
80 	snd_oxfw_stream_lock_release(oxfw);
81 	return 0;
82 }
83 
84 static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
85 {
86 	struct snd_oxfw *oxfw = substrm->rmidi->private_data;
87 
88 	guard(spinlock_irqsave)(&oxfw->lock);
89 
90 	if (up)
91 		amdtp_am824_midi_trigger(&oxfw->tx_stream,
92 					 substrm->number, substrm);
93 	else
94 		amdtp_am824_midi_trigger(&oxfw->tx_stream,
95 					 substrm->number, NULL);
96 }
97 
98 static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
99 {
100 	struct snd_oxfw *oxfw = substrm->rmidi->private_data;
101 
102 	guard(spinlock_irqsave)(&oxfw->lock);
103 
104 	if (up)
105 		amdtp_am824_midi_trigger(&oxfw->rx_stream,
106 					 substrm->number, substrm);
107 	else
108 		amdtp_am824_midi_trigger(&oxfw->rx_stream,
109 					 substrm->number, NULL);
110 }
111 
112 static void set_midi_substream_names(struct snd_oxfw *oxfw,
113 				     struct snd_rawmidi_str *str)
114 {
115 	struct snd_rawmidi_substream *subs;
116 
117 	list_for_each_entry(subs, &str->substreams, list) {
118 		scnprintf(subs->name, sizeof(subs->name),
119 			  "%s MIDI %d",
120 			  oxfw->card->shortname, subs->number + 1);
121 	}
122 }
123 
124 int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
125 {
126 	static const struct snd_rawmidi_ops capture_ops = {
127 		.open		= midi_capture_open,
128 		.close		= midi_capture_close,
129 		.trigger	= midi_capture_trigger,
130 	};
131 	static const struct snd_rawmidi_ops playback_ops = {
132 		.open		= midi_playback_open,
133 		.close		= midi_playback_close,
134 		.trigger	= midi_playback_trigger,
135 	};
136 	struct snd_rawmidi *rmidi;
137 	struct snd_rawmidi_str *str;
138 	int err;
139 
140 	if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
141 		return 0;
142 
143 	/* create midi ports */
144 	err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0,
145 			      oxfw->midi_output_ports, oxfw->midi_input_ports,
146 			      &rmidi);
147 	if (err < 0)
148 		return err;
149 
150 	snprintf(rmidi->name, sizeof(rmidi->name),
151 		 "%s MIDI", oxfw->card->shortname);
152 	rmidi->private_data = oxfw;
153 
154 	if (oxfw->midi_input_ports > 0) {
155 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
156 
157 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
158 				    &capture_ops);
159 
160 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
161 
162 		set_midi_substream_names(oxfw, str);
163 	}
164 
165 	if (oxfw->midi_output_ports > 0) {
166 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
167 
168 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
169 				    &playback_ops);
170 
171 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
172 
173 		set_midi_substream_names(oxfw, str);
174 	}
175 
176 	if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0))
177 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
178 
179 	return 0;
180 }
181