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 	mutex_lock(&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 	mutex_unlock(&dg00x->mutex);
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 	mutex_lock(&dg00x->mutex);
39 	--dg00x->substreams_counter;
40 	snd_dg00x_stream_stop_duplex(dg00x);
41 	mutex_unlock(&dg00x->mutex);
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 	unsigned long flags;
53 
54 	if (substream->rmidi->device == 0)
55 		port = substream->number;
56 	else
57 		port = 2;
58 
59 	spin_lock_irqsave(&dg00x->lock, flags);
60 
61 	if (up)
62 		amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
63 	else
64 		amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
65 
66 	spin_unlock_irqrestore(&dg00x->lock, flags);
67 }
68 
69 static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
70 				  int up)
71 {
72 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
73 	unsigned int port;
74 	unsigned long flags;
75 
76 	if (substream->rmidi->device == 0)
77 		port = substream->number;
78 	else
79 		port = 2;
80 
81 	spin_lock_irqsave(&dg00x->lock, flags);
82 
83 	if (up)
84 		amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
85 	else
86 		amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
87 
88 	spin_unlock_irqrestore(&dg00x->lock, flags);
89 }
90 
91 static void set_substream_names(struct snd_dg00x *dg00x,
92 				struct snd_rawmidi *rmidi, bool is_console)
93 {
94 	struct snd_rawmidi_substream *subs;
95 	struct snd_rawmidi_str *str;
96 	int i;
97 
98 	for (i = 0; i < 2; ++i) {
99 		str = &rmidi->streams[i];
100 
101 		list_for_each_entry(subs, &str->substreams, list) {
102 			if (!is_console) {
103 				snprintf(subs->name, sizeof(subs->name),
104 					 "%s MIDI %d",
105 					 dg00x->card->shortname,
106 					 subs->number + 1);
107 			} else {
108 				snprintf(subs->name, sizeof(subs->name),
109 					 "%s control",
110 					 dg00x->card->shortname);
111 			}
112 		}
113 	}
114 }
115 
116 static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
117 			      unsigned int in_ports, bool is_console)
118 {
119 	static const struct snd_rawmidi_ops capture_ops = {
120 		.open = midi_open,
121 		.close = midi_close,
122 		.trigger = midi_capture_trigger,
123 	};
124 	static const struct snd_rawmidi_ops playback_ops = {
125 		.open = midi_open,
126 		.close = midi_close,
127 		.trigger = midi_playback_trigger,
128 	};
129 	const char *label;
130 	struct snd_rawmidi *rmidi;
131 	int err;
132 
133 	/* Add physical midi ports. */
134 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
135 			      out_ports, in_ports, &rmidi);
136 	if (err < 0)
137 		return err;
138 	rmidi->private_data = dg00x;
139 
140 	if (!is_console)
141 		label = "%s control";
142 	else
143 		label = "%s MIDI";
144 	snprintf(rmidi->name, sizeof(rmidi->name), label,
145 		 dg00x->card->shortname);
146 
147 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
148 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
149 
150 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
151 			     SNDRV_RAWMIDI_INFO_OUTPUT |
152 			     SNDRV_RAWMIDI_INFO_DUPLEX;
153 
154 	set_substream_names(dg00x, rmidi, is_console);
155 
156 	return 0;
157 }
158 
159 int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
160 {
161 	int err;
162 
163 	/* Add physical midi ports. */
164 	err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
165 				 false);
166 	if (err < 0)
167 		return err;
168 
169 	if (dg00x->is_console)
170 		err = add_substream_pair(dg00x, 1, 1, true);
171 
172 	return err;
173 }
174