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 struct snd_rawmidi_ops midi_phys_capture_ops = {
80 	.open		= midi_phys_open,
81 	.close		= midi_phys_close,
82 	.trigger	= midi_phys_capture_trigger,
83 };
84 
85 static struct snd_rawmidi_ops midi_phys_playback_ops = {
86 	.open		= midi_phys_open,
87 	.close		= midi_phys_close,
88 	.trigger	= midi_phys_playback_trigger,
89 };
90 
91 static int midi_ctl_open(struct snd_rawmidi_substream *substream)
92 {
93 	/* Do nothing. */
94 	return 0;
95 }
96 
97 static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
98 {
99 	/* Do nothing. */
100 	return 0;
101 }
102 
103 static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
104 {
105 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
106 
107 	snd_fw_async_midi_port_finish(&dg00x->out_control);
108 
109 	return 0;
110 }
111 
112 static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
113 				     int up)
114 {
115 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
116 	unsigned long flags;
117 
118 	spin_lock_irqsave(&dg00x->lock, flags);
119 
120 	if (up)
121 		dg00x->in_control = substream;
122 	else
123 		dg00x->in_control = NULL;
124 
125 	spin_unlock_irqrestore(&dg00x->lock, flags);
126 }
127 
128 static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
129 				      int up)
130 {
131 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
132 	unsigned long flags;
133 
134 	spin_lock_irqsave(&dg00x->lock, flags);
135 
136 	if (up)
137 		snd_fw_async_midi_port_run(&dg00x->out_control, substream);
138 
139 	spin_unlock_irqrestore(&dg00x->lock, flags);
140 }
141 
142 static struct snd_rawmidi_ops midi_ctl_capture_ops = {
143 	.open		= midi_ctl_open,
144 	.close		= midi_ctl_capture_close,
145 	.trigger	= midi_ctl_capture_trigger,
146 };
147 
148 static struct snd_rawmidi_ops midi_ctl_playback_ops = {
149 	.open		= midi_ctl_open,
150 	.close		= midi_ctl_playback_close,
151 	.trigger	= midi_ctl_playback_trigger,
152 };
153 
154 static void set_midi_substream_names(struct snd_dg00x *dg00x,
155 				     struct snd_rawmidi_str *str,
156 				     bool is_ctl)
157 {
158 	struct snd_rawmidi_substream *subs;
159 
160 	list_for_each_entry(subs, &str->substreams, list) {
161 		if (!is_ctl)
162 			snprintf(subs->name, sizeof(subs->name),
163 				 "%s MIDI %d",
164 				 dg00x->card->shortname, subs->number + 1);
165 		else
166 			/* This port is for asynchronous transaction. */
167 			snprintf(subs->name, sizeof(subs->name),
168 				 "%s control",
169 				 dg00x->card->shortname);
170 	}
171 }
172 
173 int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
174 {
175 	struct snd_rawmidi *rmidi[2];
176 	struct snd_rawmidi_str *str;
177 	unsigned int i;
178 	int err;
179 
180 	/* Add physical midi ports. */
181 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
182 			DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
183 	if (err < 0)
184 		return err;
185 
186 	snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
187 		 "%s MIDI", dg00x->card->shortname);
188 
189 	snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
190 			    &midi_phys_capture_ops);
191 	snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
192 			    &midi_phys_playback_ops);
193 
194 	/* Add a pair of control midi ports. */
195 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
196 			      1, 1, &rmidi[1]);
197 	if (err < 0)
198 		return err;
199 
200 	snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
201 		 "%s control", dg00x->card->shortname);
202 
203 	snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
204 			    &midi_ctl_capture_ops);
205 	snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
206 			    &midi_ctl_playback_ops);
207 
208 	for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
209 		rmidi[i]->private_data = dg00x;
210 
211 		rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
212 		str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
213 		set_midi_substream_names(dg00x, str, i);
214 
215 		rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
216 		str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
217 		set_midi_substream_names(dg00x, str, i);
218 
219 		rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
220 	}
221 
222 	return 0;
223 }
224