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 
ca_midi_clear_rx(struct snd_ca_midi * midi)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 
ca_midi_interrupt(struct snd_ca_midi * midi,unsigned int status)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 	spin_lock(&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 	spin_unlock(&midi->input_lock);
61 
62 	spin_lock(&midi->output_lock);
63 	if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
64 		if (midi->substream_output &&
65 		    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
66 			ca_midi_write_data(midi, byte);
67 		} else {
68 			midi->interrupt_disable(midi,midi->tx_enable);
69 		}
70 	}
71 	spin_unlock(&midi->output_lock);
72 
73 }
74 
ca_midi_cmd(struct snd_ca_midi * midi,unsigned char cmd,int ack)75 static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
76 {
77 	unsigned long flags;
78 	int timeout, ok;
79 
80 	spin_lock_irqsave(&midi->input_lock, flags);
81 	ca_midi_write_data(midi, 0x00);
82 	/* ca_midi_clear_rx(midi); */
83 
84 	ca_midi_write_cmd(midi, cmd);
85 	if (ack) {
86 		ok = 0;
87 		timeout = 10000;
88 		while (!ok && timeout-- > 0) {
89 			if (ca_midi_input_avail(midi)) {
90 				if (ca_midi_read_data(midi) == midi->ack)
91 					ok = 1;
92 			}
93 		}
94 		if (!ok && ca_midi_read_data(midi) == midi->ack)
95 			ok = 1;
96 	} else {
97 		ok = 1;
98 	}
99 	spin_unlock_irqrestore(&midi->input_lock, flags);
100 	if (!ok)
101 		pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
102 			   cmd,
103 			   midi->get_dev_id_port(midi->dev_id),
104 			   ca_midi_read_stat(midi),
105 			   ca_midi_read_data(midi));
106 }
107 
ca_midi_input_open(struct snd_rawmidi_substream * substream)108 static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
109 {
110 	struct snd_ca_midi *midi = substream->rmidi->private_data;
111 	unsigned long flags;
112 
113 	if (snd_BUG_ON(!midi->dev_id))
114 		return -ENXIO;
115 	spin_lock_irqsave(&midi->open_lock, flags);
116 	midi->midi_mode |= CA_MIDI_MODE_INPUT;
117 	midi->substream_input = substream;
118 	if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
119 		spin_unlock_irqrestore(&midi->open_lock, flags);
120 		ca_midi_cmd(midi, midi->reset, 1);
121 		ca_midi_cmd(midi, midi->enter_uart, 1);
122 	} else {
123 		spin_unlock_irqrestore(&midi->open_lock, flags);
124 	}
125 	return 0;
126 }
127 
ca_midi_output_open(struct snd_rawmidi_substream * substream)128 static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
129 {
130 	struct snd_ca_midi *midi = substream->rmidi->private_data;
131 	unsigned long flags;
132 
133 	if (snd_BUG_ON(!midi->dev_id))
134 		return -ENXIO;
135 	spin_lock_irqsave(&midi->open_lock, flags);
136 	midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
137 	midi->substream_output = substream;
138 	if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
139 		spin_unlock_irqrestore(&midi->open_lock, flags);
140 		ca_midi_cmd(midi, midi->reset, 1);
141 		ca_midi_cmd(midi, midi->enter_uart, 1);
142 	} else {
143 		spin_unlock_irqrestore(&midi->open_lock, flags);
144 	}
145 	return 0;
146 }
147 
ca_midi_input_close(struct snd_rawmidi_substream * substream)148 static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
149 {
150 	struct snd_ca_midi *midi = substream->rmidi->private_data;
151 	unsigned long flags;
152 
153 	if (snd_BUG_ON(!midi->dev_id))
154 		return -ENXIO;
155 	spin_lock_irqsave(&midi->open_lock, flags);
156 	midi->interrupt_disable(midi,midi->rx_enable);
157 	midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
158 	midi->substream_input = NULL;
159 	if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
160 		spin_unlock_irqrestore(&midi->open_lock, flags);
161 		ca_midi_cmd(midi, midi->reset, 0);
162 	} else {
163 		spin_unlock_irqrestore(&midi->open_lock, flags);
164 	}
165 	return 0;
166 }
167 
ca_midi_output_close(struct snd_rawmidi_substream * substream)168 static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
169 {
170 	struct snd_ca_midi *midi = substream->rmidi->private_data;
171 	unsigned long flags;
172 
173 	if (snd_BUG_ON(!midi->dev_id))
174 		return -ENXIO;
175 
176 	spin_lock_irqsave(&midi->open_lock, flags);
177 
178 	midi->interrupt_disable(midi,midi->tx_enable);
179 	midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
180 	midi->substream_output = NULL;
181 
182 	if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
183 		spin_unlock_irqrestore(&midi->open_lock, flags);
184 		ca_midi_cmd(midi, midi->reset, 0);
185 	} else {
186 		spin_unlock_irqrestore(&midi->open_lock, flags);
187 	}
188 	return 0;
189 }
190 
ca_midi_input_trigger(struct snd_rawmidi_substream * substream,int up)191 static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
192 {
193 	struct snd_ca_midi *midi = substream->rmidi->private_data;
194 
195 	if (snd_BUG_ON(!midi->dev_id))
196 		return;
197 
198 	if (up) {
199 		midi->interrupt_enable(midi,midi->rx_enable);
200 	} else {
201 		midi->interrupt_disable(midi, midi->rx_enable);
202 	}
203 }
204 
ca_midi_output_trigger(struct snd_rawmidi_substream * substream,int up)205 static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
206 {
207 	struct snd_ca_midi *midi = substream->rmidi->private_data;
208 	unsigned long flags;
209 
210 	if (snd_BUG_ON(!midi->dev_id))
211 		return;
212 
213 	if (up) {
214 		int max = 4;
215 		unsigned char byte;
216 
217 		spin_lock_irqsave(&midi->output_lock, flags);
218 
219 		/* try to send some amount of bytes here before interrupts */
220 		while (max > 0) {
221 			if (ca_midi_output_ready(midi)) {
222 				if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
223 				    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
224 					/* no more data */
225 					spin_unlock_irqrestore(&midi->output_lock, flags);
226 					return;
227 				}
228 				ca_midi_write_data(midi, byte);
229 				max--;
230 			} else {
231 				break;
232 			}
233 		}
234 
235 		spin_unlock_irqrestore(&midi->output_lock, flags);
236 		midi->interrupt_enable(midi,midi->tx_enable);
237 
238 	} else {
239 		midi->interrupt_disable(midi,midi->tx_enable);
240 	}
241 }
242 
243 static const struct snd_rawmidi_ops ca_midi_output =
244 {
245 	.open =		ca_midi_output_open,
246 	.close =	ca_midi_output_close,
247 	.trigger =	ca_midi_output_trigger,
248 };
249 
250 static const struct snd_rawmidi_ops ca_midi_input =
251 {
252 	.open =		ca_midi_input_open,
253 	.close =	ca_midi_input_close,
254 	.trigger =	ca_midi_input_trigger,
255 };
256 
ca_midi_free(struct snd_ca_midi * midi)257 static void ca_midi_free(struct snd_ca_midi *midi)
258 {
259 	midi->interrupt = NULL;
260 	midi->interrupt_enable = NULL;
261 	midi->interrupt_disable = NULL;
262 	midi->read = NULL;
263 	midi->write = NULL;
264 	midi->get_dev_id_card = NULL;
265 	midi->get_dev_id_port = NULL;
266 	midi->rmidi = NULL;
267 }
268 
ca_rmidi_free(struct snd_rawmidi * rmidi)269 static void ca_rmidi_free(struct snd_rawmidi *rmidi)
270 {
271 	ca_midi_free(rmidi->private_data);
272 }
273 
ca_midi_init(void * dev_id,struct snd_ca_midi * midi,int device,char * name)274 int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
275 {
276 	struct snd_rawmidi *rmidi;
277 	int err;
278 
279 	if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
280 		return err;
281 
282 	midi->dev_id = dev_id;
283 	midi->interrupt = ca_midi_interrupt;
284 
285 	spin_lock_init(&midi->open_lock);
286 	spin_lock_init(&midi->input_lock);
287 	spin_lock_init(&midi->output_lock);
288 
289 	strcpy(rmidi->name, name);
290 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
291 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
292 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
293 	                     SNDRV_RAWMIDI_INFO_INPUT |
294 	                     SNDRV_RAWMIDI_INFO_DUPLEX;
295 	rmidi->private_data = midi;
296 	rmidi->private_free = ca_rmidi_free;
297 
298 	midi->rmidi = rmidi;
299 	return 0;
300 }
301 
302