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