Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / sound / firewire / digi00x / digi00x-midi.c
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);
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 }