xref: /linux/sound/firewire/digi00x/digi00x-midi.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
4  *
5  * Copyright (c) 2014-2015 Takashi Sakamoto
6  */
7 
8 #include "digi00x.h"
9 
10 static int midi_open(struct snd_rawmidi_substream *substream)
11 {
12 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
13 	int err;
14 
15 	err = snd_dg00x_stream_lock_try(dg00x);
16 	if (err < 0)
17 		return err;
18 
19 	scoped_guard(mutex, &dg00x->mutex) {
20 		err = snd_dg00x_stream_reserve_duplex(dg00x, 0, 0, 0);
21 		if (err >= 0) {
22 			++dg00x->substreams_counter;
23 			err = snd_dg00x_stream_start_duplex(dg00x);
24 			if (err < 0)
25 				--dg00x->substreams_counter;
26 		}
27 	}
28 	if (err < 0)
29 		snd_dg00x_stream_lock_release(dg00x);
30 
31 	return err;
32 }
33 
34 static int midi_close(struct snd_rawmidi_substream *substream)
35 {
36 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
37 
38 	scoped_guard(mutex, &dg00x->mutex) {
39 		--dg00x->substreams_counter;
40 		snd_dg00x_stream_stop_duplex(dg00x);
41 	}
42 
43 	snd_dg00x_stream_lock_release(dg00x);
44 	return 0;
45 }
46 
47 static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
48 				 int up)
49 {
50 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
51 	unsigned int port;
52 
53 	if (substream->rmidi->device == 0)
54 		port = substream->number;
55 	else
56 		port = 2;
57 
58 	guard(spinlock_irqsave)(&dg00x->lock);
59 
60 	if (up)
61 		amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
62 	else
63 		amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
64 }
65 
66 static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
67 				  int up)
68 {
69 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
70 	unsigned int port;
71 
72 	if (substream->rmidi->device == 0)
73 		port = substream->number;
74 	else
75 		port = 2;
76 
77 	guard(spinlock_irqsave)(&dg00x->lock);
78 
79 	if (up)
80 		amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
81 	else
82 		amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
83 }
84 
85 static void set_substream_names(struct snd_dg00x *dg00x,
86 				struct snd_rawmidi *rmidi, bool is_console)
87 {
88 	struct snd_rawmidi_substream *subs;
89 	struct snd_rawmidi_str *str;
90 	int i;
91 
92 	for (i = 0; i < 2; ++i) {
93 		str = &rmidi->streams[i];
94 
95 		list_for_each_entry(subs, &str->substreams, list) {
96 			if (!is_console) {
97 				scnprintf(subs->name, sizeof(subs->name),
98 					  "%s MIDI %d",
99 					  dg00x->card->shortname,
100 					  subs->number + 1);
101 			} else {
102 				scnprintf(subs->name, sizeof(subs->name),
103 					  "%s control",
104 					  dg00x->card->shortname);
105 			}
106 		}
107 	}
108 }
109 
110 static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
111 			      unsigned int in_ports, bool is_console)
112 {
113 	static const struct snd_rawmidi_ops capture_ops = {
114 		.open = midi_open,
115 		.close = midi_close,
116 		.trigger = midi_capture_trigger,
117 	};
118 	static const struct snd_rawmidi_ops playback_ops = {
119 		.open = midi_open,
120 		.close = midi_close,
121 		.trigger = midi_playback_trigger,
122 	};
123 	const char *label;
124 	struct snd_rawmidi *rmidi;
125 	int err;
126 
127 	/* Add physical midi ports. */
128 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
129 			      out_ports, in_ports, &rmidi);
130 	if (err < 0)
131 		return err;
132 	rmidi->private_data = dg00x;
133 
134 	if (!is_console)
135 		label = "%s control";
136 	else
137 		label = "%s MIDI";
138 	snprintf(rmidi->name, sizeof(rmidi->name), label,
139 		 dg00x->card->shortname);
140 
141 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
142 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
143 
144 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
145 			     SNDRV_RAWMIDI_INFO_OUTPUT |
146 			     SNDRV_RAWMIDI_INFO_DUPLEX;
147 
148 	set_substream_names(dg00x, rmidi, is_console);
149 
150 	return 0;
151 }
152 
153 int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
154 {
155 	int err;
156 
157 	/* Add physical midi ports. */
158 	err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
159 				 false);
160 	if (err < 0)
161 		return err;
162 
163 	if (dg00x->is_console)
164 		err = add_substream_pair(dg00x, 1, 1, true);
165 
166 	return err;
167 }
168