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
11static 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
30static 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
43static 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
61static 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
79static 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
85static 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
91static int midi_ctl_open(struct snd_rawmidi_substream *substream)
92{
93	/* Do nothing. */
94	return 0;
95}
96
97static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
98{
99	/* Do nothing. */
100	return 0;
101}
102
103static 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
112static 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
128static 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
142static 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
148static 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
154static 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
173int 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