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