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