1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * oxfw_midi.c - a part of driver for OXFW970/971 based devices 4 * 5 * Copyright (c) 2014 Takashi Sakamoto 6 */ 7 8 #include "oxfw.h" 9 10 static int midi_capture_open(struct snd_rawmidi_substream *substream) 11 { 12 struct snd_oxfw *oxfw = substream->rmidi->private_data; 13 int err; 14 15 err = snd_oxfw_stream_lock_try(oxfw); 16 if (err < 0) 17 return err; 18 19 scoped_guard(mutex, &oxfw->mutex) { 20 err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0); 21 if (err >= 0) { 22 ++oxfw->substreams_count; 23 err = snd_oxfw_stream_start_duplex(oxfw); 24 if (err < 0) 25 --oxfw->substreams_count; 26 } 27 } 28 29 if (err < 0) 30 snd_oxfw_stream_lock_release(oxfw); 31 32 return err; 33 } 34 35 static int midi_playback_open(struct snd_rawmidi_substream *substream) 36 { 37 struct snd_oxfw *oxfw = substream->rmidi->private_data; 38 int err; 39 40 err = snd_oxfw_stream_lock_try(oxfw); 41 if (err < 0) 42 return err; 43 44 scoped_guard(mutex, &oxfw->mutex) { 45 err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0); 46 if (err >= 0) { 47 ++oxfw->substreams_count; 48 err = snd_oxfw_stream_start_duplex(oxfw); 49 } 50 } 51 52 if (err < 0) 53 snd_oxfw_stream_lock_release(oxfw); 54 55 return err; 56 } 57 58 static int midi_capture_close(struct snd_rawmidi_substream *substream) 59 { 60 struct snd_oxfw *oxfw = substream->rmidi->private_data; 61 62 scoped_guard(mutex, &oxfw->mutex) { 63 --oxfw->substreams_count; 64 snd_oxfw_stream_stop_duplex(oxfw); 65 } 66 67 snd_oxfw_stream_lock_release(oxfw); 68 return 0; 69 } 70 71 static int midi_playback_close(struct snd_rawmidi_substream *substream) 72 { 73 struct snd_oxfw *oxfw = substream->rmidi->private_data; 74 75 scoped_guard(mutex, &oxfw->mutex) { 76 --oxfw->substreams_count; 77 snd_oxfw_stream_stop_duplex(oxfw); 78 } 79 80 snd_oxfw_stream_lock_release(oxfw); 81 return 0; 82 } 83 84 static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) 85 { 86 struct snd_oxfw *oxfw = substrm->rmidi->private_data; 87 88 guard(spinlock_irqsave)(&oxfw->lock); 89 90 if (up) 91 amdtp_am824_midi_trigger(&oxfw->tx_stream, 92 substrm->number, substrm); 93 else 94 amdtp_am824_midi_trigger(&oxfw->tx_stream, 95 substrm->number, NULL); 96 } 97 98 static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) 99 { 100 struct snd_oxfw *oxfw = substrm->rmidi->private_data; 101 102 guard(spinlock_irqsave)(&oxfw->lock); 103 104 if (up) 105 amdtp_am824_midi_trigger(&oxfw->rx_stream, 106 substrm->number, substrm); 107 else 108 amdtp_am824_midi_trigger(&oxfw->rx_stream, 109 substrm->number, NULL); 110 } 111 112 static void set_midi_substream_names(struct snd_oxfw *oxfw, 113 struct snd_rawmidi_str *str) 114 { 115 struct snd_rawmidi_substream *subs; 116 117 list_for_each_entry(subs, &str->substreams, list) { 118 scnprintf(subs->name, sizeof(subs->name), 119 "%s MIDI %d", 120 oxfw->card->shortname, subs->number + 1); 121 } 122 } 123 124 int snd_oxfw_create_midi(struct snd_oxfw *oxfw) 125 { 126 static const struct snd_rawmidi_ops capture_ops = { 127 .open = midi_capture_open, 128 .close = midi_capture_close, 129 .trigger = midi_capture_trigger, 130 }; 131 static const struct snd_rawmidi_ops playback_ops = { 132 .open = midi_playback_open, 133 .close = midi_playback_close, 134 .trigger = midi_playback_trigger, 135 }; 136 struct snd_rawmidi *rmidi; 137 struct snd_rawmidi_str *str; 138 int err; 139 140 if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0) 141 return 0; 142 143 /* create midi ports */ 144 err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0, 145 oxfw->midi_output_ports, oxfw->midi_input_ports, 146 &rmidi); 147 if (err < 0) 148 return err; 149 150 snprintf(rmidi->name, sizeof(rmidi->name), 151 "%s MIDI", oxfw->card->shortname); 152 rmidi->private_data = oxfw; 153 154 if (oxfw->midi_input_ports > 0) { 155 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 156 157 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 158 &capture_ops); 159 160 str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; 161 162 set_midi_substream_names(oxfw, str); 163 } 164 165 if (oxfw->midi_output_ports > 0) { 166 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; 167 168 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 169 &playback_ops); 170 171 str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; 172 173 set_midi_substream_names(oxfw, str); 174 } 175 176 if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0)) 177 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 178 179 return 0; 180 } 181