1 /*
2  * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
3  *
4  * Copyright (c) 2014-2015 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8 
9 #include "digi00x.h"
10 
11 static int midi_phys_open(struct snd_rawmidi_substream *substream)
12 {
13 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
14 	int err;
15 
16 	err = snd_dg00x_stream_lock_try(dg00x);
17 	if (err < 0)
18 		return err;
19 
20 	mutex_lock(&dg00x->mutex);
21 	dg00x->substreams_counter++;
22 	err = snd_dg00x_stream_start_duplex(dg00x, 0);
23 	mutex_unlock(&dg00x->mutex);
24 	if (err < 0)
25 		snd_dg00x_stream_lock_release(dg00x);
26 
27 	return err;
28 }
29 
30 static int midi_phys_close(struct snd_rawmidi_substream *substream)
31 {
32 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
33 
34 	mutex_lock(&dg00x->mutex);
35 	dg00x->substreams_counter--;
36 	snd_dg00x_stream_stop_duplex(dg00x);
37 	mutex_unlock(&dg00x->mutex);
38 
39 	snd_dg00x_stream_lock_release(dg00x);
40 	return 0;
41 }
42 
43 static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
44 				      int up)
45 {
46 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
47 	unsigned long flags;
48 
49 	spin_lock_irqsave(&dg00x->lock, flags);
50 
51 	if (up)
52 		amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
53 				       substream);
54 	else
55 		amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
56 				       NULL);
57 
58 	spin_unlock_irqrestore(&dg00x->lock, flags);
59 }
60 
61 static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
62 				       int up)
63 {
64 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
65 	unsigned long flags;
66 
67 	spin_lock_irqsave(&dg00x->lock, flags);
68 
69 	if (up)
70 		amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
71 				       substream);
72 	else
73 		amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
74 				       NULL);
75 
76 	spin_unlock_irqrestore(&dg00x->lock, flags);
77 }
78 
79 static int midi_ctl_open(struct snd_rawmidi_substream *substream)
80 {
81 	/* Do nothing. */
82 	return 0;
83 }
84 
85 static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
86 {
87 	/* Do nothing. */
88 	return 0;
89 }
90 
91 static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
92 {
93 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
94 
95 	snd_fw_async_midi_port_finish(&dg00x->out_control);
96 
97 	return 0;
98 }
99 
100 static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
101 				     int up)
102 {
103 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
104 	unsigned long flags;
105 
106 	spin_lock_irqsave(&dg00x->lock, flags);
107 
108 	if (up)
109 		dg00x->in_control = substream;
110 	else
111 		dg00x->in_control = NULL;
112 
113 	spin_unlock_irqrestore(&dg00x->lock, flags);
114 }
115 
116 static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
117 				      int up)
118 {
119 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
120 	unsigned long flags;
121 
122 	spin_lock_irqsave(&dg00x->lock, flags);
123 
124 	if (up)
125 		snd_fw_async_midi_port_run(&dg00x->out_control, substream);
126 
127 	spin_unlock_irqrestore(&dg00x->lock, flags);
128 }
129 
130 static void set_midi_substream_names(struct snd_dg00x *dg00x,
131 				     struct snd_rawmidi_str *str,
132 				     bool is_ctl)
133 {
134 	struct snd_rawmidi_substream *subs;
135 
136 	list_for_each_entry(subs, &str->substreams, list) {
137 		if (!is_ctl)
138 			snprintf(subs->name, sizeof(subs->name),
139 				 "%s MIDI %d",
140 				 dg00x->card->shortname, subs->number + 1);
141 		else
142 			/* This port is for asynchronous transaction. */
143 			snprintf(subs->name, sizeof(subs->name),
144 				 "%s control",
145 				 dg00x->card->shortname);
146 	}
147 }
148 
149 int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
150 {
151 	static const struct snd_rawmidi_ops phys_capture_ops = {
152 		.open		= midi_phys_open,
153 		.close		= midi_phys_close,
154 		.trigger	= midi_phys_capture_trigger,
155 	};
156 	static const struct snd_rawmidi_ops phys_playback_ops = {
157 		.open		= midi_phys_open,
158 		.close		= midi_phys_close,
159 		.trigger	= midi_phys_playback_trigger,
160 	};
161 	static const struct snd_rawmidi_ops ctl_capture_ops = {
162 		.open		= midi_ctl_open,
163 		.close		= midi_ctl_capture_close,
164 		.trigger	= midi_ctl_capture_trigger,
165 	};
166 	static const struct snd_rawmidi_ops ctl_playback_ops = {
167 		.open		= midi_ctl_open,
168 		.close		= midi_ctl_playback_close,
169 		.trigger	= midi_ctl_playback_trigger,
170 	};
171 	struct snd_rawmidi *rmidi[2];
172 	struct snd_rawmidi_str *str;
173 	unsigned int i;
174 	int err;
175 
176 	/* Add physical midi ports. */
177 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
178 			DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
179 	if (err < 0)
180 		return err;
181 
182 	snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
183 		 "%s MIDI", dg00x->card->shortname);
184 
185 	snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
186 			    &phys_capture_ops);
187 	snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
188 			    &phys_playback_ops);
189 
190 	/* Add a pair of control midi ports. */
191 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
192 			      1, 1, &rmidi[1]);
193 	if (err < 0)
194 		return err;
195 
196 	snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
197 		 "%s control", dg00x->card->shortname);
198 
199 	snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
200 			    &ctl_capture_ops);
201 	snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
202 			    &ctl_playback_ops);
203 
204 	for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
205 		rmidi[i]->private_data = dg00x;
206 
207 		rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
208 		str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
209 		set_midi_substream_names(dg00x, str, i);
210 
211 		rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
212 		str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
213 		set_midi_substream_names(dg00x, str, i);
214 
215 		rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
216 	}
217 
218 	return 0;
219 }
220