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, rate: 0, frames_per_period: 0, frames_per_buffer: 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(lock: &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(lock: &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(s: &dg00x->tx_stream, port, midi: substream); |
63 | else |
64 | amdtp_dot_midi_trigger(s: &dg00x->tx_stream, port, NULL); |
65 | |
66 | spin_unlock_irqrestore(lock: &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(s: &dg00x->rx_stream, port, midi: substream); |
85 | else |
86 | amdtp_dot_midi_trigger(s: &dg00x->rx_stream, port, NULL); |
87 | |
88 | spin_unlock_irqrestore(lock: &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 | scnprintf(buf: subs->name, size: sizeof(subs->name), |
104 | fmt: "%s MIDI %d" , |
105 | dg00x->card->shortname, |
106 | subs->number + 1); |
107 | } else { |
108 | scnprintf(buf: subs->name, size: sizeof(subs->name), |
109 | fmt: "%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(card: dg00x->card, id: dg00x->card->driver, device: is_console, |
135 | output_count: out_ports, input_count: in_ports, rmidi: &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(buf: rmidi->name, size: sizeof(rmidi->name), fmt: label, |
145 | dg00x->card->shortname); |
146 | |
147 | snd_rawmidi_set_ops(rmidi, stream: SNDRV_RAWMIDI_STREAM_OUTPUT, ops: &playback_ops); |
148 | snd_rawmidi_set_ops(rmidi, stream: SNDRV_RAWMIDI_STREAM_INPUT, ops: &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 | is_console: false); |
166 | if (err < 0) |
167 | return err; |
168 | |
169 | if (dg00x->is_console) |
170 | err = add_substream_pair(dg00x, out_ports: 1, in_ports: 1, is_console: true); |
171 | |
172 | return err; |
173 | } |
174 | |