1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * oxfw_midi.c - a part of driver for OXFW970/971 based devices
4 *
5 * Copyright (c) 2014 Takashi Sakamoto
6 */
7
8#include "oxfw.h"
9
10static int midi_capture_open(struct snd_rawmidi_substream *substream)
11{
12	struct snd_oxfw *oxfw = substream->rmidi->private_data;
13	int err;
14
15	err = snd_oxfw_stream_lock_try(oxfw);
16	if (err < 0)
17		return err;
18
19	mutex_lock(&oxfw->mutex);
20
21	err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0);
22	if (err >= 0) {
23		++oxfw->substreams_count;
24		err = snd_oxfw_stream_start_duplex(oxfw);
25		if (err < 0)
26			--oxfw->substreams_count;
27	}
28
29	mutex_unlock(&oxfw->mutex);
30
31	if (err < 0)
32		snd_oxfw_stream_lock_release(oxfw);
33
34	return err;
35}
36
37static int midi_playback_open(struct snd_rawmidi_substream *substream)
38{
39	struct snd_oxfw *oxfw = substream->rmidi->private_data;
40	int err;
41
42	err = snd_oxfw_stream_lock_try(oxfw);
43	if (err < 0)
44		return err;
45
46	mutex_lock(&oxfw->mutex);
47
48	err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0);
49	if (err >= 0) {
50		++oxfw->substreams_count;
51		err = snd_oxfw_stream_start_duplex(oxfw);
52	}
53
54	mutex_unlock(&oxfw->mutex);
55
56	if (err < 0)
57		snd_oxfw_stream_lock_release(oxfw);
58
59	return err;
60}
61
62static int midi_capture_close(struct snd_rawmidi_substream *substream)
63{
64	struct snd_oxfw *oxfw = substream->rmidi->private_data;
65
66	mutex_lock(&oxfw->mutex);
67
68	--oxfw->substreams_count;
69	snd_oxfw_stream_stop_duplex(oxfw);
70
71	mutex_unlock(&oxfw->mutex);
72
73	snd_oxfw_stream_lock_release(oxfw);
74	return 0;
75}
76
77static int midi_playback_close(struct snd_rawmidi_substream *substream)
78{
79	struct snd_oxfw *oxfw = substream->rmidi->private_data;
80
81	mutex_lock(&oxfw->mutex);
82
83	--oxfw->substreams_count;
84	snd_oxfw_stream_stop_duplex(oxfw);
85
86	mutex_unlock(&oxfw->mutex);
87
88	snd_oxfw_stream_lock_release(oxfw);
89	return 0;
90}
91
92static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
93{
94	struct snd_oxfw *oxfw = substrm->rmidi->private_data;
95	unsigned long flags;
96
97	spin_lock_irqsave(&oxfw->lock, flags);
98
99	if (up)
100		amdtp_am824_midi_trigger(&oxfw->tx_stream,
101					 substrm->number, substrm);
102	else
103		amdtp_am824_midi_trigger(&oxfw->tx_stream,
104					 substrm->number, NULL);
105
106	spin_unlock_irqrestore(&oxfw->lock, flags);
107}
108
109static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
110{
111	struct snd_oxfw *oxfw = substrm->rmidi->private_data;
112	unsigned long flags;
113
114	spin_lock_irqsave(&oxfw->lock, flags);
115
116	if (up)
117		amdtp_am824_midi_trigger(&oxfw->rx_stream,
118					 substrm->number, substrm);
119	else
120		amdtp_am824_midi_trigger(&oxfw->rx_stream,
121					 substrm->number, NULL);
122
123	spin_unlock_irqrestore(&oxfw->lock, flags);
124}
125
126static void set_midi_substream_names(struct snd_oxfw *oxfw,
127				     struct snd_rawmidi_str *str)
128{
129	struct snd_rawmidi_substream *subs;
130
131	list_for_each_entry(subs, &str->substreams, list) {
132		scnprintf(subs->name, sizeof(subs->name),
133			  "%s MIDI %d",
134			  oxfw->card->shortname, subs->number + 1);
135	}
136}
137
138int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
139{
140	static const struct snd_rawmidi_ops capture_ops = {
141		.open		= midi_capture_open,
142		.close		= midi_capture_close,
143		.trigger	= midi_capture_trigger,
144	};
145	static const struct snd_rawmidi_ops playback_ops = {
146		.open		= midi_playback_open,
147		.close		= midi_playback_close,
148		.trigger	= midi_playback_trigger,
149	};
150	struct snd_rawmidi *rmidi;
151	struct snd_rawmidi_str *str;
152	int err;
153
154	if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
155		return 0;
156
157	/* create midi ports */
158	err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0,
159			      oxfw->midi_output_ports, oxfw->midi_input_ports,
160			      &rmidi);
161	if (err < 0)
162		return err;
163
164	snprintf(rmidi->name, sizeof(rmidi->name),
165		 "%s MIDI", oxfw->card->shortname);
166	rmidi->private_data = oxfw;
167
168	if (oxfw->midi_input_ports > 0) {
169		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
170
171		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
172				    &capture_ops);
173
174		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
175
176		set_midi_substream_names(oxfw, str);
177	}
178
179	if (oxfw->midi_output_ports > 0) {
180		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
181
182		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
183				    &playback_ops);
184
185		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
186
187		set_midi_substream_names(oxfw, str);
188	}
189
190	if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0))
191		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
192
193	return 0;
194}
195