1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * motu-midi.h - a part of driver for MOTU FireWire series
4 *
5 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6 */
7#include "motu.h"
8
9static int midi_open(struct snd_rawmidi_substream *substream)
10{
11	struct snd_motu *motu = substream->rmidi->private_data;
12	int err;
13
14	err = snd_motu_stream_lock_try(motu);
15	if (err < 0)
16		return err;
17
18	mutex_lock(&motu->mutex);
19
20	err = snd_motu_stream_reserve_duplex(motu, 0, 0, 0);
21	if (err >= 0) {
22		++motu->substreams_counter;
23		err = snd_motu_stream_start_duplex(motu);
24		if (err < 0)
25			--motu->substreams_counter;
26	}
27
28	mutex_unlock(&motu->mutex);
29
30	if (err < 0)
31		snd_motu_stream_lock_release(motu);
32
33	return err;
34}
35
36static int midi_close(struct snd_rawmidi_substream *substream)
37{
38	struct snd_motu *motu = substream->rmidi->private_data;
39
40	mutex_lock(&motu->mutex);
41
42	--motu->substreams_counter;
43	snd_motu_stream_stop_duplex(motu);
44
45	mutex_unlock(&motu->mutex);
46
47	snd_motu_stream_lock_release(motu);
48	return 0;
49}
50
51static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
52{
53	struct snd_motu *motu = substrm->rmidi->private_data;
54	unsigned long flags;
55
56	spin_lock_irqsave(&motu->lock, flags);
57
58	if (up)
59		amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
60					substrm);
61	else
62		amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
63					NULL);
64
65	spin_unlock_irqrestore(&motu->lock, flags);
66}
67
68static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
69{
70	struct snd_motu *motu = substrm->rmidi->private_data;
71	unsigned long flags;
72
73	spin_lock_irqsave(&motu->lock, flags);
74
75	if (up)
76		amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
77					substrm);
78	else
79		amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
80					NULL);
81
82	spin_unlock_irqrestore(&motu->lock, flags);
83}
84
85static void set_midi_substream_names(struct snd_motu *motu,
86				     struct snd_rawmidi_str *str)
87{
88	struct snd_rawmidi_substream *subs;
89
90	list_for_each_entry(subs, &str->substreams, list) {
91		scnprintf(subs->name, sizeof(subs->name),
92			  "%s MIDI %d", motu->card->shortname, subs->number + 1);
93	}
94}
95
96int snd_motu_create_midi_devices(struct snd_motu *motu)
97{
98	static const struct snd_rawmidi_ops capture_ops = {
99		.open		= midi_open,
100		.close		= midi_close,
101		.trigger	= midi_capture_trigger,
102	};
103	static const struct snd_rawmidi_ops playback_ops = {
104		.open		= midi_open,
105		.close		= midi_close,
106		.trigger	= midi_playback_trigger,
107	};
108	struct snd_rawmidi *rmidi;
109	struct snd_rawmidi_str *str;
110	int err;
111
112	/* create midi ports */
113	err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
114	if (err < 0)
115		return err;
116
117	snprintf(rmidi->name, sizeof(rmidi->name),
118		 "%s MIDI", motu->card->shortname);
119	rmidi->private_data = motu;
120
121	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
122			     SNDRV_RAWMIDI_INFO_OUTPUT |
123			     SNDRV_RAWMIDI_INFO_DUPLEX;
124
125	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
126			    &capture_ops);
127	str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
128	set_midi_substream_names(motu, str);
129
130	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
131			    &playback_ops);
132	str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
133	set_midi_substream_names(motu, str);
134
135	return 0;
136}
137