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_capture_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 		goto end;
18 
19 	mutex_lock(&efw->mutex);
20 	efw->capture_substreams++;
21 	err = snd_efw_stream_start_duplex(efw, 0);
22 	mutex_unlock(&efw->mutex);
23 	if (err < 0)
24 		snd_efw_stream_lock_release(efw);
25 
26 end:
27 	return err;
28 }
29 
30 static int midi_playback_open(struct snd_rawmidi_substream *substream)
31 {
32 	struct snd_efw *efw = substream->rmidi->private_data;
33 	int err;
34 
35 	err = snd_efw_stream_lock_try(efw);
36 	if (err < 0)
37 		goto end;
38 
39 	mutex_lock(&efw->mutex);
40 	efw->playback_substreams++;
41 	err = snd_efw_stream_start_duplex(efw, 0);
42 	mutex_unlock(&efw->mutex);
43 	if (err < 0)
44 		snd_efw_stream_lock_release(efw);
45 end:
46 	return err;
47 }
48 
49 static int midi_capture_close(struct snd_rawmidi_substream *substream)
50 {
51 	struct snd_efw *efw = substream->rmidi->private_data;
52 
53 	mutex_lock(&efw->mutex);
54 	efw->capture_substreams--;
55 	snd_efw_stream_stop_duplex(efw);
56 	mutex_unlock(&efw->mutex);
57 
58 	snd_efw_stream_lock_release(efw);
59 	return 0;
60 }
61 
62 static int midi_playback_close(struct snd_rawmidi_substream *substream)
63 {
64 	struct snd_efw *efw = substream->rmidi->private_data;
65 
66 	mutex_lock(&efw->mutex);
67 	efw->playback_substreams--;
68 	snd_efw_stream_stop_duplex(efw);
69 	mutex_unlock(&efw->mutex);
70 
71 	snd_efw_stream_lock_release(efw);
72 	return 0;
73 }
74 
75 static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
76 {
77 	struct snd_efw *efw = substrm->rmidi->private_data;
78 	unsigned long flags;
79 
80 	spin_lock_irqsave(&efw->lock, flags);
81 
82 	if (up)
83 		amdtp_am824_midi_trigger(&efw->tx_stream,
84 					  substrm->number, substrm);
85 	else
86 		amdtp_am824_midi_trigger(&efw->tx_stream,
87 					  substrm->number, NULL);
88 
89 	spin_unlock_irqrestore(&efw->lock, flags);
90 }
91 
92 static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
93 {
94 	struct snd_efw *efw = substrm->rmidi->private_data;
95 	unsigned long flags;
96 
97 	spin_lock_irqsave(&efw->lock, flags);
98 
99 	if (up)
100 		amdtp_am824_midi_trigger(&efw->rx_stream,
101 					 substrm->number, substrm);
102 	else
103 		amdtp_am824_midi_trigger(&efw->rx_stream,
104 					 substrm->number, NULL);
105 
106 	spin_unlock_irqrestore(&efw->lock, flags);
107 }
108 
109 static void set_midi_substream_names(struct snd_efw *efw,
110 				     struct snd_rawmidi_str *str)
111 {
112 	struct snd_rawmidi_substream *subs;
113 
114 	list_for_each_entry(subs, &str->substreams, list) {
115 		snprintf(subs->name, sizeof(subs->name),
116 			 "%s MIDI %d", efw->card->shortname, subs->number + 1);
117 	}
118 }
119 
120 int snd_efw_create_midi_devices(struct snd_efw *efw)
121 {
122 	static const struct snd_rawmidi_ops capture_ops = {
123 		.open		= midi_capture_open,
124 		.close		= midi_capture_close,
125 		.trigger	= midi_capture_trigger,
126 	};
127 	static const struct snd_rawmidi_ops playback_ops = {
128 		.open		= midi_playback_open,
129 		.close		= midi_playback_close,
130 		.trigger	= midi_playback_trigger,
131 	};
132 	struct snd_rawmidi *rmidi;
133 	struct snd_rawmidi_str *str;
134 	int err;
135 
136 	/* create midi ports */
137 	err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
138 			      efw->midi_out_ports, efw->midi_in_ports,
139 			      &rmidi);
140 	if (err < 0)
141 		return err;
142 
143 	snprintf(rmidi->name, sizeof(rmidi->name),
144 		 "%s MIDI", efw->card->shortname);
145 	rmidi->private_data = efw;
146 
147 	if (efw->midi_in_ports > 0) {
148 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
149 
150 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
151 				    &capture_ops);
152 
153 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
154 
155 		set_midi_substream_names(efw, str);
156 	}
157 
158 	if (efw->midi_out_ports > 0) {
159 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
160 
161 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
162 				    &playback_ops);
163 
164 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
165 
166 		set_midi_substream_names(efw, str);
167 	}
168 
169 	if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
170 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
171 
172 	return 0;
173 }
174