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