11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28a5afd29SJames Courtier-Dutton /*
38a5afd29SJames Courtier-Dutton * Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
48a5afd29SJames Courtier-Dutton * Creative Audio MIDI, for the CA0106 Driver
58a5afd29SJames Courtier-Dutton * Version: 0.0.1
68a5afd29SJames Courtier-Dutton *
78a5afd29SJames Courtier-Dutton * Changelog:
88a5afd29SJames Courtier-Dutton * Implementation is based on mpu401 and emu10k1x and
98a5afd29SJames Courtier-Dutton * tested with ca0106.
10c1017a4cSJaroslav Kysela * mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz>
117b4260f2SJames Courtier-Dutton * emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
128a5afd29SJames Courtier-Dutton */
138a5afd29SJames Courtier-Dutton
148a5afd29SJames Courtier-Dutton #include <linux/spinlock.h>
158a5afd29SJames Courtier-Dutton #include <sound/core.h>
168a5afd29SJames Courtier-Dutton #include <sound/rawmidi.h>
178a5afd29SJames Courtier-Dutton
188a5afd29SJames Courtier-Dutton #include "ca_midi.h"
198a5afd29SJames Courtier-Dutton
208a5afd29SJames Courtier-Dutton #define ca_midi_write_data(midi, data) midi->write(midi, data, 0)
218a5afd29SJames Courtier-Dutton #define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1)
228a5afd29SJames Courtier-Dutton #define ca_midi_read_data(midi) midi->read(midi, 0)
238a5afd29SJames Courtier-Dutton #define ca_midi_read_stat(midi) midi->read(midi, 1)
248a5afd29SJames Courtier-Dutton #define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail))
258a5afd29SJames Courtier-Dutton #define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready))
268a5afd29SJames Courtier-Dutton
ca_midi_clear_rx(struct snd_ca_midi * midi)27e4a3d145STakashi Iwai static void ca_midi_clear_rx(struct snd_ca_midi *midi)
288a5afd29SJames Courtier-Dutton {
298a5afd29SJames Courtier-Dutton int timeout = 100000;
308a5afd29SJames Courtier-Dutton for (; timeout > 0 && ca_midi_input_avail(midi); timeout--)
318a5afd29SJames Courtier-Dutton ca_midi_read_data(midi);
328a5afd29SJames Courtier-Dutton #ifdef CONFIG_SND_DEBUG
338a5afd29SJames Courtier-Dutton if (timeout <= 0)
3474103227STakashi Iwai pr_err("ca_midi_clear_rx: timeout (status = 0x%x)\n",
35e4a3d145STakashi Iwai ca_midi_read_stat(midi));
368a5afd29SJames Courtier-Dutton #endif
378a5afd29SJames Courtier-Dutton }
388a5afd29SJames Courtier-Dutton
ca_midi_interrupt(struct snd_ca_midi * midi,unsigned int status)39e4a3d145STakashi Iwai static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status)
40e4a3d145STakashi Iwai {
418a5afd29SJames Courtier-Dutton unsigned char byte;
428a5afd29SJames Courtier-Dutton
438a5afd29SJames Courtier-Dutton if (midi->rmidi == NULL) {
448a5afd29SJames Courtier-Dutton midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable);
458a5afd29SJames Courtier-Dutton return;
468a5afd29SJames Courtier-Dutton }
478a5afd29SJames Courtier-Dutton
488a5afd29SJames Courtier-Dutton spin_lock(&midi->input_lock);
498a5afd29SJames Courtier-Dutton if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
508a5afd29SJames Courtier-Dutton if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
518a5afd29SJames Courtier-Dutton ca_midi_clear_rx(midi);
528a5afd29SJames Courtier-Dutton } else {
538a5afd29SJames Courtier-Dutton byte = ca_midi_read_data(midi);
548a5afd29SJames Courtier-Dutton if(midi->substream_input)
558a5afd29SJames Courtier-Dutton snd_rawmidi_receive(midi->substream_input, &byte, 1);
568a5afd29SJames Courtier-Dutton
578a5afd29SJames Courtier-Dutton
588a5afd29SJames Courtier-Dutton }
598a5afd29SJames Courtier-Dutton }
608a5afd29SJames Courtier-Dutton spin_unlock(&midi->input_lock);
618a5afd29SJames Courtier-Dutton
628a5afd29SJames Courtier-Dutton spin_lock(&midi->output_lock);
638a5afd29SJames Courtier-Dutton if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
648a5afd29SJames Courtier-Dutton if (midi->substream_output &&
658a5afd29SJames Courtier-Dutton snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
668a5afd29SJames Courtier-Dutton ca_midi_write_data(midi, byte);
678a5afd29SJames Courtier-Dutton } else {
688a5afd29SJames Courtier-Dutton midi->interrupt_disable(midi,midi->tx_enable);
698a5afd29SJames Courtier-Dutton }
708a5afd29SJames Courtier-Dutton }
718a5afd29SJames Courtier-Dutton spin_unlock(&midi->output_lock);
728a5afd29SJames Courtier-Dutton
738a5afd29SJames Courtier-Dutton }
748a5afd29SJames Courtier-Dutton
ca_midi_cmd(struct snd_ca_midi * midi,unsigned char cmd,int ack)75e4a3d145STakashi Iwai static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
768a5afd29SJames Courtier-Dutton {
778a5afd29SJames Courtier-Dutton unsigned long flags;
788a5afd29SJames Courtier-Dutton int timeout, ok;
798a5afd29SJames Courtier-Dutton
808a5afd29SJames Courtier-Dutton spin_lock_irqsave(&midi->input_lock, flags);
818a5afd29SJames Courtier-Dutton ca_midi_write_data(midi, 0x00);
828a5afd29SJames Courtier-Dutton /* ca_midi_clear_rx(midi); */
838a5afd29SJames Courtier-Dutton
848a5afd29SJames Courtier-Dutton ca_midi_write_cmd(midi, cmd);
858a5afd29SJames Courtier-Dutton if (ack) {
868a5afd29SJames Courtier-Dutton ok = 0;
878a5afd29SJames Courtier-Dutton timeout = 10000;
888a5afd29SJames Courtier-Dutton while (!ok && timeout-- > 0) {
898a5afd29SJames Courtier-Dutton if (ca_midi_input_avail(midi)) {
908a5afd29SJames Courtier-Dutton if (ca_midi_read_data(midi) == midi->ack)
918a5afd29SJames Courtier-Dutton ok = 1;
928a5afd29SJames Courtier-Dutton }
938a5afd29SJames Courtier-Dutton }
948a5afd29SJames Courtier-Dutton if (!ok && ca_midi_read_data(midi) == midi->ack)
958a5afd29SJames Courtier-Dutton ok = 1;
968a5afd29SJames Courtier-Dutton } else {
978a5afd29SJames Courtier-Dutton ok = 1;
988a5afd29SJames Courtier-Dutton }
998a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->input_lock, flags);
1008a5afd29SJames Courtier-Dutton if (!ok)
10174103227STakashi Iwai pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
1028a5afd29SJames Courtier-Dutton cmd,
1038a5afd29SJames Courtier-Dutton midi->get_dev_id_port(midi->dev_id),
1048a5afd29SJames Courtier-Dutton ca_midi_read_stat(midi),
1058a5afd29SJames Courtier-Dutton ca_midi_read_data(midi));
1068a5afd29SJames Courtier-Dutton }
1078a5afd29SJames Courtier-Dutton
ca_midi_input_open(struct snd_rawmidi_substream * substream)108e4a3d145STakashi Iwai static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
1098a5afd29SJames Courtier-Dutton {
110e4a3d145STakashi Iwai struct snd_ca_midi *midi = substream->rmidi->private_data;
1118a5afd29SJames Courtier-Dutton unsigned long flags;
1128a5afd29SJames Courtier-Dutton
113da3cec35STakashi Iwai if (snd_BUG_ON(!midi->dev_id))
114da3cec35STakashi Iwai return -ENXIO;
1158a5afd29SJames Courtier-Dutton spin_lock_irqsave(&midi->open_lock, flags);
1168a5afd29SJames Courtier-Dutton midi->midi_mode |= CA_MIDI_MODE_INPUT;
1178a5afd29SJames Courtier-Dutton midi->substream_input = substream;
1188a5afd29SJames Courtier-Dutton if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
1198a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1208a5afd29SJames Courtier-Dutton ca_midi_cmd(midi, midi->reset, 1);
1218a5afd29SJames Courtier-Dutton ca_midi_cmd(midi, midi->enter_uart, 1);
1228a5afd29SJames Courtier-Dutton } else {
1238a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1248a5afd29SJames Courtier-Dutton }
1258a5afd29SJames Courtier-Dutton return 0;
1268a5afd29SJames Courtier-Dutton }
1278a5afd29SJames Courtier-Dutton
ca_midi_output_open(struct snd_rawmidi_substream * substream)128e4a3d145STakashi Iwai static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
1298a5afd29SJames Courtier-Dutton {
130e4a3d145STakashi Iwai struct snd_ca_midi *midi = substream->rmidi->private_data;
1318a5afd29SJames Courtier-Dutton unsigned long flags;
1328a5afd29SJames Courtier-Dutton
133da3cec35STakashi Iwai if (snd_BUG_ON(!midi->dev_id))
134da3cec35STakashi Iwai return -ENXIO;
1358a5afd29SJames Courtier-Dutton spin_lock_irqsave(&midi->open_lock, flags);
1368a5afd29SJames Courtier-Dutton midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
1378a5afd29SJames Courtier-Dutton midi->substream_output = substream;
1388a5afd29SJames Courtier-Dutton if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
1398a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1408a5afd29SJames Courtier-Dutton ca_midi_cmd(midi, midi->reset, 1);
1418a5afd29SJames Courtier-Dutton ca_midi_cmd(midi, midi->enter_uart, 1);
1428a5afd29SJames Courtier-Dutton } else {
1438a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1448a5afd29SJames Courtier-Dutton }
1458a5afd29SJames Courtier-Dutton return 0;
1468a5afd29SJames Courtier-Dutton }
1478a5afd29SJames Courtier-Dutton
ca_midi_input_close(struct snd_rawmidi_substream * substream)148e4a3d145STakashi Iwai static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
1498a5afd29SJames Courtier-Dutton {
150e4a3d145STakashi Iwai struct snd_ca_midi *midi = substream->rmidi->private_data;
1518a5afd29SJames Courtier-Dutton unsigned long flags;
1528a5afd29SJames Courtier-Dutton
153da3cec35STakashi Iwai if (snd_BUG_ON(!midi->dev_id))
154da3cec35STakashi Iwai return -ENXIO;
1558a5afd29SJames Courtier-Dutton spin_lock_irqsave(&midi->open_lock, flags);
1568a5afd29SJames Courtier-Dutton midi->interrupt_disable(midi,midi->rx_enable);
1578a5afd29SJames Courtier-Dutton midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
1588a5afd29SJames Courtier-Dutton midi->substream_input = NULL;
1598a5afd29SJames Courtier-Dutton if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
1608a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1618a5afd29SJames Courtier-Dutton ca_midi_cmd(midi, midi->reset, 0);
1628a5afd29SJames Courtier-Dutton } else {
1638a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1648a5afd29SJames Courtier-Dutton }
1658a5afd29SJames Courtier-Dutton return 0;
1668a5afd29SJames Courtier-Dutton }
1678a5afd29SJames Courtier-Dutton
ca_midi_output_close(struct snd_rawmidi_substream * substream)168e4a3d145STakashi Iwai static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
1698a5afd29SJames Courtier-Dutton {
170e4a3d145STakashi Iwai struct snd_ca_midi *midi = substream->rmidi->private_data;
1718a5afd29SJames Courtier-Dutton unsigned long flags;
172da3cec35STakashi Iwai
173da3cec35STakashi Iwai if (snd_BUG_ON(!midi->dev_id))
174da3cec35STakashi Iwai return -ENXIO;
1758a5afd29SJames Courtier-Dutton
1768a5afd29SJames Courtier-Dutton spin_lock_irqsave(&midi->open_lock, flags);
1778a5afd29SJames Courtier-Dutton
1788a5afd29SJames Courtier-Dutton midi->interrupt_disable(midi,midi->tx_enable);
1798a5afd29SJames Courtier-Dutton midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
1808a5afd29SJames Courtier-Dutton midi->substream_output = NULL;
1818a5afd29SJames Courtier-Dutton
1828a5afd29SJames Courtier-Dutton if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
1838a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1848a5afd29SJames Courtier-Dutton ca_midi_cmd(midi, midi->reset, 0);
1858a5afd29SJames Courtier-Dutton } else {
1868a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->open_lock, flags);
1878a5afd29SJames Courtier-Dutton }
1888a5afd29SJames Courtier-Dutton return 0;
1898a5afd29SJames Courtier-Dutton }
1908a5afd29SJames Courtier-Dutton
ca_midi_input_trigger(struct snd_rawmidi_substream * substream,int up)191e4a3d145STakashi Iwai static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
1928a5afd29SJames Courtier-Dutton {
193e4a3d145STakashi Iwai struct snd_ca_midi *midi = substream->rmidi->private_data;
194da3cec35STakashi Iwai
195da3cec35STakashi Iwai if (snd_BUG_ON(!midi->dev_id))
196da3cec35STakashi Iwai return;
1978a5afd29SJames Courtier-Dutton
1988a5afd29SJames Courtier-Dutton if (up) {
1998a5afd29SJames Courtier-Dutton midi->interrupt_enable(midi,midi->rx_enable);
2008a5afd29SJames Courtier-Dutton } else {
2018a5afd29SJames Courtier-Dutton midi->interrupt_disable(midi, midi->rx_enable);
2028a5afd29SJames Courtier-Dutton }
2038a5afd29SJames Courtier-Dutton }
2048a5afd29SJames Courtier-Dutton
ca_midi_output_trigger(struct snd_rawmidi_substream * substream,int up)205e4a3d145STakashi Iwai static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
2068a5afd29SJames Courtier-Dutton {
207e4a3d145STakashi Iwai struct snd_ca_midi *midi = substream->rmidi->private_data;
2088a5afd29SJames Courtier-Dutton unsigned long flags;
2098a5afd29SJames Courtier-Dutton
210da3cec35STakashi Iwai if (snd_BUG_ON(!midi->dev_id))
211da3cec35STakashi Iwai return;
2128a5afd29SJames Courtier-Dutton
2138a5afd29SJames Courtier-Dutton if (up) {
2148a5afd29SJames Courtier-Dutton int max = 4;
2158a5afd29SJames Courtier-Dutton unsigned char byte;
2168a5afd29SJames Courtier-Dutton
2178a5afd29SJames Courtier-Dutton spin_lock_irqsave(&midi->output_lock, flags);
2188a5afd29SJames Courtier-Dutton
2198a5afd29SJames Courtier-Dutton /* try to send some amount of bytes here before interrupts */
2208a5afd29SJames Courtier-Dutton while (max > 0) {
2218a5afd29SJames Courtier-Dutton if (ca_midi_output_ready(midi)) {
2228a5afd29SJames Courtier-Dutton if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
2238a5afd29SJames Courtier-Dutton snd_rawmidi_transmit(substream, &byte, 1) != 1) {
2248a5afd29SJames Courtier-Dutton /* no more data */
2258a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->output_lock, flags);
2268a5afd29SJames Courtier-Dutton return;
2278a5afd29SJames Courtier-Dutton }
2288a5afd29SJames Courtier-Dutton ca_midi_write_data(midi, byte);
2298a5afd29SJames Courtier-Dutton max--;
2308a5afd29SJames Courtier-Dutton } else {
2318a5afd29SJames Courtier-Dutton break;
2328a5afd29SJames Courtier-Dutton }
2338a5afd29SJames Courtier-Dutton }
2348a5afd29SJames Courtier-Dutton
2358a5afd29SJames Courtier-Dutton spin_unlock_irqrestore(&midi->output_lock, flags);
2368a5afd29SJames Courtier-Dutton midi->interrupt_enable(midi,midi->tx_enable);
2378a5afd29SJames Courtier-Dutton
2388a5afd29SJames Courtier-Dutton } else {
2398a5afd29SJames Courtier-Dutton midi->interrupt_disable(midi,midi->tx_enable);
2408a5afd29SJames Courtier-Dutton }
2418a5afd29SJames Courtier-Dutton }
2428a5afd29SJames Courtier-Dutton
243485885b9STakashi Iwai static const struct snd_rawmidi_ops ca_midi_output =
2448a5afd29SJames Courtier-Dutton {
2458a5afd29SJames Courtier-Dutton .open = ca_midi_output_open,
2468a5afd29SJames Courtier-Dutton .close = ca_midi_output_close,
2478a5afd29SJames Courtier-Dutton .trigger = ca_midi_output_trigger,
2488a5afd29SJames Courtier-Dutton };
2498a5afd29SJames Courtier-Dutton
250485885b9STakashi Iwai static const struct snd_rawmidi_ops ca_midi_input =
2518a5afd29SJames Courtier-Dutton {
2528a5afd29SJames Courtier-Dutton .open = ca_midi_input_open,
2538a5afd29SJames Courtier-Dutton .close = ca_midi_input_close,
2548a5afd29SJames Courtier-Dutton .trigger = ca_midi_input_trigger,
2558a5afd29SJames Courtier-Dutton };
2568a5afd29SJames Courtier-Dutton
ca_midi_free(struct snd_ca_midi * midi)257e4a3d145STakashi Iwai static void ca_midi_free(struct snd_ca_midi *midi)
258e4a3d145STakashi Iwai {
2598a5afd29SJames Courtier-Dutton midi->interrupt = NULL;
2608a5afd29SJames Courtier-Dutton midi->interrupt_enable = NULL;
2618a5afd29SJames Courtier-Dutton midi->interrupt_disable = NULL;
2628a5afd29SJames Courtier-Dutton midi->read = NULL;
2638a5afd29SJames Courtier-Dutton midi->write = NULL;
2648a5afd29SJames Courtier-Dutton midi->get_dev_id_card = NULL;
2658a5afd29SJames Courtier-Dutton midi->get_dev_id_port = NULL;
2668a5afd29SJames Courtier-Dutton midi->rmidi = NULL;
2678a5afd29SJames Courtier-Dutton }
2688a5afd29SJames Courtier-Dutton
ca_rmidi_free(struct snd_rawmidi * rmidi)269e4a3d145STakashi Iwai static void ca_rmidi_free(struct snd_rawmidi *rmidi)
2708a5afd29SJames Courtier-Dutton {
271e4a3d145STakashi Iwai ca_midi_free(rmidi->private_data);
2728a5afd29SJames Courtier-Dutton }
2738a5afd29SJames Courtier-Dutton
ca_midi_init(void * dev_id,struct snd_ca_midi * midi,int device,char * name)274e23e7a14SBill Pemberton int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
2758a5afd29SJames Courtier-Dutton {
276e4a3d145STakashi Iwai struct snd_rawmidi *rmidi;
2778a5afd29SJames Courtier-Dutton int err;
2788a5afd29SJames Courtier-Dutton
279*59c39cd3STakashi Iwai err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi);
280*59c39cd3STakashi Iwai if (err < 0)
2818a5afd29SJames Courtier-Dutton return err;
2828a5afd29SJames Courtier-Dutton
2838a5afd29SJames Courtier-Dutton midi->dev_id = dev_id;
2848a5afd29SJames Courtier-Dutton midi->interrupt = ca_midi_interrupt;
2858a5afd29SJames Courtier-Dutton
2868a5afd29SJames Courtier-Dutton spin_lock_init(&midi->open_lock);
2878a5afd29SJames Courtier-Dutton spin_lock_init(&midi->input_lock);
2888a5afd29SJames Courtier-Dutton spin_lock_init(&midi->output_lock);
2898a5afd29SJames Courtier-Dutton
2908a5afd29SJames Courtier-Dutton strcpy(rmidi->name, name);
2918a5afd29SJames Courtier-Dutton snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
2928a5afd29SJames Courtier-Dutton snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
2938a5afd29SJames Courtier-Dutton rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
2948a5afd29SJames Courtier-Dutton SNDRV_RAWMIDI_INFO_INPUT |
2958a5afd29SJames Courtier-Dutton SNDRV_RAWMIDI_INFO_DUPLEX;
2968a5afd29SJames Courtier-Dutton rmidi->private_data = midi;
2978a5afd29SJames Courtier-Dutton rmidi->private_free = ca_rmidi_free;
2988a5afd29SJames Courtier-Dutton
2998a5afd29SJames Courtier-Dutton midi->rmidi = rmidi;
3008a5afd29SJames Courtier-Dutton return 0;
3018a5afd29SJames Courtier-Dutton }
3028a5afd29SJames Courtier-Dutton
303