1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de> 4 * Creative Audio MIDI, for the CA0106 Driver 5 * Version: 0.0.1 6 * 7 * Changelog: 8 * Implementation is based on mpu401 and emu10k1x and 9 * tested with ca0106. 10 * mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz> 11 * emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 12 */ 13 14 #include <linux/spinlock.h> 15 #include <sound/core.h> 16 #include <sound/rawmidi.h> 17 18 #include "ca_midi.h" 19 20 #define ca_midi_write_data(midi, data) midi->write(midi, data, 0) 21 #define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1) 22 #define ca_midi_read_data(midi) midi->read(midi, 0) 23 #define ca_midi_read_stat(midi) midi->read(midi, 1) 24 #define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail)) 25 #define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready)) 26 27 static void ca_midi_clear_rx(struct snd_ca_midi *midi) 28 { 29 int timeout = 100000; 30 for (; timeout > 0 && ca_midi_input_avail(midi); timeout--) 31 ca_midi_read_data(midi); 32 #ifdef CONFIG_SND_DEBUG 33 if (timeout <= 0) 34 pr_err("ca_midi_clear_rx: timeout (status = 0x%x)\n", 35 ca_midi_read_stat(midi)); 36 #endif 37 } 38 39 static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status) 40 { 41 unsigned char byte; 42 43 if (midi->rmidi == NULL) { 44 midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable); 45 return; 46 } 47 48 scoped_guard(spinlock, &midi->input_lock) { 49 if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) { 50 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 51 ca_midi_clear_rx(midi); 52 } else { 53 byte = ca_midi_read_data(midi); 54 if (midi->substream_input) 55 snd_rawmidi_receive(midi->substream_input, &byte, 1); 56 } 57 } 58 } 59 60 scoped_guard(spinlock, &midi->output_lock) { 61 if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) { 62 if (midi->substream_output && 63 snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { 64 ca_midi_write_data(midi, byte); 65 } else { 66 midi->interrupt_disable(midi, midi->tx_enable); 67 } 68 } 69 } 70 } 71 72 static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack) 73 { 74 int timeout, ok; 75 76 scoped_guard(spinlock_irqsave, &midi->input_lock) { 77 ca_midi_write_data(midi, 0x00); 78 /* ca_midi_clear_rx(midi); */ 79 80 ca_midi_write_cmd(midi, cmd); 81 if (ack) { 82 ok = 0; 83 timeout = 10000; 84 while (!ok && timeout-- > 0) { 85 if (ca_midi_input_avail(midi)) { 86 if (ca_midi_read_data(midi) == midi->ack) 87 ok = 1; 88 } 89 } 90 if (!ok && ca_midi_read_data(midi) == midi->ack) 91 ok = 1; 92 } else { 93 ok = 1; 94 } 95 } 96 if (!ok) 97 pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n", 98 cmd, 99 midi->get_dev_id_port(midi->dev_id), 100 ca_midi_read_stat(midi), 101 ca_midi_read_data(midi)); 102 } 103 104 static int ca_midi_input_open(struct snd_rawmidi_substream *substream) 105 { 106 struct snd_ca_midi *midi = substream->rmidi->private_data; 107 108 if (snd_BUG_ON(!midi->dev_id)) 109 return -ENXIO; 110 scoped_guard(spinlock_irqsave, &midi->open_lock) { 111 midi->midi_mode |= CA_MIDI_MODE_INPUT; 112 midi->substream_input = substream; 113 if (midi->midi_mode & CA_MIDI_MODE_OUTPUT) 114 return 0; 115 } 116 ca_midi_cmd(midi, midi->reset, 1); 117 ca_midi_cmd(midi, midi->enter_uart, 1); 118 return 0; 119 } 120 121 static int ca_midi_output_open(struct snd_rawmidi_substream *substream) 122 { 123 struct snd_ca_midi *midi = substream->rmidi->private_data; 124 125 if (snd_BUG_ON(!midi->dev_id)) 126 return -ENXIO; 127 scoped_guard(spinlock_irqsave, &midi->open_lock) { 128 midi->midi_mode |= CA_MIDI_MODE_OUTPUT; 129 midi->substream_output = substream; 130 if (midi->midi_mode & CA_MIDI_MODE_INPUT) 131 return 0; 132 } 133 ca_midi_cmd(midi, midi->reset, 1); 134 ca_midi_cmd(midi, midi->enter_uart, 1); 135 return 0; 136 } 137 138 static int ca_midi_input_close(struct snd_rawmidi_substream *substream) 139 { 140 struct snd_ca_midi *midi = substream->rmidi->private_data; 141 142 if (snd_BUG_ON(!midi->dev_id)) 143 return -ENXIO; 144 scoped_guard(spinlock_irqsave, &midi->open_lock) { 145 midi->interrupt_disable(midi, midi->rx_enable); 146 midi->midi_mode &= ~CA_MIDI_MODE_INPUT; 147 midi->substream_input = NULL; 148 if (midi->midi_mode & CA_MIDI_MODE_OUTPUT) 149 return 0; 150 } 151 ca_midi_cmd(midi, midi->reset, 0); 152 return 0; 153 } 154 155 static int ca_midi_output_close(struct snd_rawmidi_substream *substream) 156 { 157 struct snd_ca_midi *midi = substream->rmidi->private_data; 158 159 if (snd_BUG_ON(!midi->dev_id)) 160 return -ENXIO; 161 162 scoped_guard(spinlock_irqsave, &midi->open_lock) { 163 midi->interrupt_disable(midi, midi->tx_enable); 164 midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT; 165 midi->substream_output = NULL; 166 if (midi->midi_mode & CA_MIDI_MODE_INPUT) 167 return 0; 168 } 169 ca_midi_cmd(midi, midi->reset, 0); 170 return 0; 171 } 172 173 static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 174 { 175 struct snd_ca_midi *midi = substream->rmidi->private_data; 176 177 if (snd_BUG_ON(!midi->dev_id)) 178 return; 179 180 if (up) { 181 midi->interrupt_enable(midi,midi->rx_enable); 182 } else { 183 midi->interrupt_disable(midi, midi->rx_enable); 184 } 185 } 186 187 static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 188 { 189 struct snd_ca_midi *midi = substream->rmidi->private_data; 190 191 if (snd_BUG_ON(!midi->dev_id)) 192 return; 193 194 if (up) { 195 int max = 4; 196 unsigned char byte; 197 198 scoped_guard(spinlock_irqsave, &midi->output_lock) { 199 200 /* try to send some amount of bytes here before interrupts */ 201 while (max > 0) { 202 if (ca_midi_output_ready(midi)) { 203 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) || 204 snd_rawmidi_transmit(substream, &byte, 1) != 1) { 205 /* no more data */ 206 return; 207 } 208 ca_midi_write_data(midi, byte); 209 max--; 210 } else { 211 break; 212 } 213 } 214 } 215 midi->interrupt_enable(midi,midi->tx_enable); 216 217 } else { 218 midi->interrupt_disable(midi,midi->tx_enable); 219 } 220 } 221 222 static const struct snd_rawmidi_ops ca_midi_output = 223 { 224 .open = ca_midi_output_open, 225 .close = ca_midi_output_close, 226 .trigger = ca_midi_output_trigger, 227 }; 228 229 static const struct snd_rawmidi_ops ca_midi_input = 230 { 231 .open = ca_midi_input_open, 232 .close = ca_midi_input_close, 233 .trigger = ca_midi_input_trigger, 234 }; 235 236 static void ca_midi_free(struct snd_ca_midi *midi) 237 { 238 midi->interrupt = NULL; 239 midi->interrupt_enable = NULL; 240 midi->interrupt_disable = NULL; 241 midi->read = NULL; 242 midi->write = NULL; 243 midi->get_dev_id_card = NULL; 244 midi->get_dev_id_port = NULL; 245 midi->rmidi = NULL; 246 } 247 248 static void ca_rmidi_free(struct snd_rawmidi *rmidi) 249 { 250 ca_midi_free(rmidi->private_data); 251 } 252 253 int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) 254 { 255 struct snd_rawmidi *rmidi; 256 int err; 257 258 err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi); 259 if (err < 0) 260 return err; 261 262 midi->dev_id = dev_id; 263 midi->interrupt = ca_midi_interrupt; 264 265 spin_lock_init(&midi->open_lock); 266 spin_lock_init(&midi->input_lock); 267 spin_lock_init(&midi->output_lock); 268 269 strscpy(rmidi->name, name); 270 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); 271 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); 272 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 273 SNDRV_RAWMIDI_INFO_INPUT | 274 SNDRV_RAWMIDI_INFO_DUPLEX; 275 rmidi->private_data = midi; 276 rmidi->private_free = ca_rmidi_free; 277 278 midi->rmidi = rmidi; 279 return 0; 280 } 281 282