xref: /openbmc/linux/sound/firewire/oxfw/oxfw-scs1x.c (revision da607e19)
1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23f47152aSTakashi Sakamoto /*
33f47152aSTakashi Sakamoto  * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
43f47152aSTakashi Sakamoto  *
53f47152aSTakashi Sakamoto  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
63f47152aSTakashi Sakamoto  * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
73f47152aSTakashi Sakamoto  */
83f47152aSTakashi Sakamoto 
93f47152aSTakashi Sakamoto #include "oxfw.h"
103f47152aSTakashi Sakamoto 
11e3315b43STakashi Sakamoto #define HSS1394_ADDRESS			0xc007dedadadaULL
12e3315b43STakashi Sakamoto #define HSS1394_MAX_PACKET_SIZE		64
1313b8b78cSTakashi Sakamoto #define HSS1394_TAG_USER_DATA		0x00
14e3315b43STakashi Sakamoto #define HSS1394_TAG_CHANGE_ADDRESS	0xf1
15e3315b43STakashi Sakamoto 
16e3315b43STakashi Sakamoto struct fw_scs1x {
17e3315b43STakashi Sakamoto 	struct fw_address_handler hss_handler;
1813b8b78cSTakashi Sakamoto 	u8 input_escape_count;
1913b8b78cSTakashi Sakamoto 	struct snd_rawmidi_substream *input;
20d7d20e77STakashi Sakamoto 
21d7d20e77STakashi Sakamoto 	/* For MIDI playback. */
22d7d20e77STakashi Sakamoto 	struct snd_rawmidi_substream *output;
23d7d20e77STakashi Sakamoto 	bool output_idle;
24d7d20e77STakashi Sakamoto 	u8 output_status;
25d7d20e77STakashi Sakamoto 	u8 output_bytes;
26d7d20e77STakashi Sakamoto 	bool output_escaped;
27d7d20e77STakashi Sakamoto 	bool output_escape_high_nibble;
28ea790053STakashi Sakamoto 	struct work_struct work;
29d7d20e77STakashi Sakamoto 	wait_queue_head_t idle_wait;
30d7d20e77STakashi Sakamoto 	u8 buffer[HSS1394_MAX_PACKET_SIZE];
31d7d20e77STakashi Sakamoto 	bool transaction_running;
32d7d20e77STakashi Sakamoto 	struct fw_transaction transaction;
33b4c23ab1STakashi Sakamoto 	unsigned int transaction_bytes;
34956dea9eSTakashi Sakamoto 	bool error;
35d7d20e77STakashi Sakamoto 	struct fw_device *fw_dev;
36e3315b43STakashi Sakamoto };
37e3315b43STakashi Sakamoto 
3813b8b78cSTakashi Sakamoto static const u8 sysex_escape_prefix[] = {
3913b8b78cSTakashi Sakamoto 	0xf0,			/* SysEx begin */
4013b8b78cSTakashi Sakamoto 	0x00, 0x01, 0x60,	/* Stanton DJ */
4113b8b78cSTakashi Sakamoto 	0x48, 0x53, 0x53,	/* "HSS" */
4213b8b78cSTakashi Sakamoto };
4313b8b78cSTakashi Sakamoto 
midi_input_escaped_byte(struct snd_rawmidi_substream * stream,u8 byte)4413b8b78cSTakashi Sakamoto static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
4513b8b78cSTakashi Sakamoto 				    u8 byte)
4613b8b78cSTakashi Sakamoto {
4713b8b78cSTakashi Sakamoto 	u8 nibbles[2];
4813b8b78cSTakashi Sakamoto 
4913b8b78cSTakashi Sakamoto 	nibbles[0] = byte >> 4;
5013b8b78cSTakashi Sakamoto 	nibbles[1] = byte & 0x0f;
5113b8b78cSTakashi Sakamoto 	snd_rawmidi_receive(stream, nibbles, 2);
5213b8b78cSTakashi Sakamoto }
5313b8b78cSTakashi Sakamoto 
midi_input_byte(struct fw_scs1x * scs,struct snd_rawmidi_substream * stream,u8 byte)5413b8b78cSTakashi Sakamoto static void midi_input_byte(struct fw_scs1x *scs,
5513b8b78cSTakashi Sakamoto 			    struct snd_rawmidi_substream *stream, u8 byte)
5613b8b78cSTakashi Sakamoto {
5713b8b78cSTakashi Sakamoto 	const u8 eox = 0xf7;
5813b8b78cSTakashi Sakamoto 
5913b8b78cSTakashi Sakamoto 	if (scs->input_escape_count > 0) {
6013b8b78cSTakashi Sakamoto 		midi_input_escaped_byte(stream, byte);
6113b8b78cSTakashi Sakamoto 		scs->input_escape_count--;
6213b8b78cSTakashi Sakamoto 		if (scs->input_escape_count == 0)
6313b8b78cSTakashi Sakamoto 			snd_rawmidi_receive(stream, &eox, sizeof(eox));
6413b8b78cSTakashi Sakamoto 	} else if (byte == 0xf9) {
6513b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, sysex_escape_prefix,
6613b8b78cSTakashi Sakamoto 				    ARRAY_SIZE(sysex_escape_prefix));
6713b8b78cSTakashi Sakamoto 		midi_input_escaped_byte(stream, 0x00);
6813b8b78cSTakashi Sakamoto 		midi_input_escaped_byte(stream, 0xf9);
6913b8b78cSTakashi Sakamoto 		scs->input_escape_count = 3;
7013b8b78cSTakashi Sakamoto 	} else {
7113b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, &byte, 1);
7213b8b78cSTakashi Sakamoto 	}
7313b8b78cSTakashi Sakamoto }
7413b8b78cSTakashi Sakamoto 
midi_input_packet(struct fw_scs1x * scs,struct snd_rawmidi_substream * stream,const u8 * data,unsigned int bytes)7513b8b78cSTakashi Sakamoto static void midi_input_packet(struct fw_scs1x *scs,
7613b8b78cSTakashi Sakamoto 			      struct snd_rawmidi_substream *stream,
7713b8b78cSTakashi Sakamoto 			      const u8 *data, unsigned int bytes)
7813b8b78cSTakashi Sakamoto {
7913b8b78cSTakashi Sakamoto 	unsigned int i;
8013b8b78cSTakashi Sakamoto 	const u8 eox = 0xf7;
8113b8b78cSTakashi Sakamoto 
8213b8b78cSTakashi Sakamoto 	if (data[0] == HSS1394_TAG_USER_DATA) {
8313b8b78cSTakashi Sakamoto 		for (i = 1; i < bytes; ++i)
8413b8b78cSTakashi Sakamoto 			midi_input_byte(scs, stream, data[i]);
8513b8b78cSTakashi Sakamoto 	} else {
8613b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, sysex_escape_prefix,
8713b8b78cSTakashi Sakamoto 				    ARRAY_SIZE(sysex_escape_prefix));
8813b8b78cSTakashi Sakamoto 		for (i = 0; i < bytes; ++i)
8913b8b78cSTakashi Sakamoto 			midi_input_escaped_byte(stream, data[i]);
9013b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, &eox, sizeof(eox));
9113b8b78cSTakashi Sakamoto 	}
9213b8b78cSTakashi Sakamoto }
9313b8b78cSTakashi Sakamoto 
handle_hss(struct fw_card * card,struct fw_request * request,int tcode,int destination,int source,int generation,unsigned long long offset,void * data,size_t length,void * callback_data)94e3315b43STakashi Sakamoto static void handle_hss(struct fw_card *card, struct fw_request *request,
95e3315b43STakashi Sakamoto 		       int tcode, int destination, int source, int generation,
96e3315b43STakashi Sakamoto 		       unsigned long long offset, void *data, size_t length,
97e3315b43STakashi Sakamoto 		       void *callback_data)
98e3315b43STakashi Sakamoto {
9913b8b78cSTakashi Sakamoto 	struct fw_scs1x *scs = callback_data;
10013b8b78cSTakashi Sakamoto 	struct snd_rawmidi_substream *stream;
10113b8b78cSTakashi Sakamoto 	int rcode;
10213b8b78cSTakashi Sakamoto 
10313b8b78cSTakashi Sakamoto 	if (offset != scs->hss_handler.offset) {
10413b8b78cSTakashi Sakamoto 		rcode = RCODE_ADDRESS_ERROR;
10513b8b78cSTakashi Sakamoto 		goto end;
10613b8b78cSTakashi Sakamoto 	}
10713b8b78cSTakashi Sakamoto 	if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
10813b8b78cSTakashi Sakamoto 	    tcode != TCODE_WRITE_BLOCK_REQUEST) {
10913b8b78cSTakashi Sakamoto 		rcode = RCODE_TYPE_ERROR;
11013b8b78cSTakashi Sakamoto 		goto end;
11113b8b78cSTakashi Sakamoto 	}
11213b8b78cSTakashi Sakamoto 
11313b8b78cSTakashi Sakamoto 	if (length >= 1) {
1146aa7de05SMark Rutland 		stream = READ_ONCE(scs->input);
11513b8b78cSTakashi Sakamoto 		if (stream)
11613b8b78cSTakashi Sakamoto 			midi_input_packet(scs, stream, data, length);
11713b8b78cSTakashi Sakamoto 	}
11813b8b78cSTakashi Sakamoto 
11913b8b78cSTakashi Sakamoto 	rcode = RCODE_COMPLETE;
12013b8b78cSTakashi Sakamoto end:
12113b8b78cSTakashi Sakamoto 	fw_send_response(card, request, rcode);
122e3315b43STakashi Sakamoto }
123e3315b43STakashi Sakamoto 
scs_write_callback(struct fw_card * card,int rcode,void * data,size_t length,void * callback_data)124d7d20e77STakashi Sakamoto static void scs_write_callback(struct fw_card *card, int rcode,
125d7d20e77STakashi Sakamoto 			       void *data, size_t length, void *callback_data)
126d7d20e77STakashi Sakamoto {
127d7d20e77STakashi Sakamoto 	struct fw_scs1x *scs = callback_data;
128d7d20e77STakashi Sakamoto 
129956dea9eSTakashi Sakamoto 	if (!rcode_is_permanent_error(rcode)) {
130956dea9eSTakashi Sakamoto 		/* Don't retry for this data. */
131956dea9eSTakashi Sakamoto 		if (rcode == RCODE_COMPLETE)
132b4c23ab1STakashi Sakamoto 			scs->transaction_bytes = 0;
133956dea9eSTakashi Sakamoto 	} else {
134956dea9eSTakashi Sakamoto 		scs->error = true;
135956dea9eSTakashi Sakamoto 	}
136d7d20e77STakashi Sakamoto 
137d7d20e77STakashi Sakamoto 	scs->transaction_running = false;
138ea790053STakashi Sakamoto 	schedule_work(&scs->work);
139d7d20e77STakashi Sakamoto }
140d7d20e77STakashi Sakamoto 
is_valid_running_status(u8 status)141d7d20e77STakashi Sakamoto static bool is_valid_running_status(u8 status)
142d7d20e77STakashi Sakamoto {
143d7d20e77STakashi Sakamoto 	return status >= 0x80 && status <= 0xef;
144d7d20e77STakashi Sakamoto }
145d7d20e77STakashi Sakamoto 
is_one_byte_cmd(u8 status)146d7d20e77STakashi Sakamoto static bool is_one_byte_cmd(u8 status)
147d7d20e77STakashi Sakamoto {
148d7d20e77STakashi Sakamoto 	return status == 0xf6 ||
149d7d20e77STakashi Sakamoto 	       status >= 0xf8;
150d7d20e77STakashi Sakamoto }
151d7d20e77STakashi Sakamoto 
is_two_bytes_cmd(u8 status)152d7d20e77STakashi Sakamoto static bool is_two_bytes_cmd(u8 status)
153d7d20e77STakashi Sakamoto {
154d7d20e77STakashi Sakamoto 	return (status >= 0xc0 && status <= 0xdf) ||
155d7d20e77STakashi Sakamoto 	       status == 0xf1 ||
156d7d20e77STakashi Sakamoto 	       status == 0xf3;
157d7d20e77STakashi Sakamoto }
158d7d20e77STakashi Sakamoto 
is_three_bytes_cmd(u8 status)159d7d20e77STakashi Sakamoto static bool is_three_bytes_cmd(u8 status)
160d7d20e77STakashi Sakamoto {
161d7d20e77STakashi Sakamoto 	return (status >= 0x80 && status <= 0xbf) ||
162d7d20e77STakashi Sakamoto 	       (status >= 0xe0 && status <= 0xef) ||
163d7d20e77STakashi Sakamoto 	       status == 0xf2;
164d7d20e77STakashi Sakamoto }
165d7d20e77STakashi Sakamoto 
is_invalid_cmd(u8 status)166d7d20e77STakashi Sakamoto static bool is_invalid_cmd(u8 status)
167d7d20e77STakashi Sakamoto {
168d7d20e77STakashi Sakamoto 	return status == 0xf4 ||
169d7d20e77STakashi Sakamoto 	       status == 0xf5 ||
170d7d20e77STakashi Sakamoto 	       status == 0xf9 ||
171d7d20e77STakashi Sakamoto 	       status == 0xfd;
172d7d20e77STakashi Sakamoto }
173d7d20e77STakashi Sakamoto 
scs_output_work(struct work_struct * work)174ea790053STakashi Sakamoto static void scs_output_work(struct work_struct *work)
175d7d20e77STakashi Sakamoto {
176ea790053STakashi Sakamoto 	struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
177d7d20e77STakashi Sakamoto 	struct snd_rawmidi_substream *stream;
178d7d20e77STakashi Sakamoto 	unsigned int i;
179d7d20e77STakashi Sakamoto 	u8 byte;
180d7d20e77STakashi Sakamoto 	int generation;
181d7d20e77STakashi Sakamoto 
182d7d20e77STakashi Sakamoto 	if (scs->transaction_running)
183d7d20e77STakashi Sakamoto 		return;
184d7d20e77STakashi Sakamoto 
1856aa7de05SMark Rutland 	stream = READ_ONCE(scs->output);
186956dea9eSTakashi Sakamoto 	if (!stream || scs->error) {
187d7d20e77STakashi Sakamoto 		scs->output_idle = true;
188d7d20e77STakashi Sakamoto 		wake_up(&scs->idle_wait);
189d7d20e77STakashi Sakamoto 		return;
190d7d20e77STakashi Sakamoto 	}
191d7d20e77STakashi Sakamoto 
192b4c23ab1STakashi Sakamoto 	if (scs->transaction_bytes > 0)
193b4c23ab1STakashi Sakamoto 		goto retry;
194b4c23ab1STakashi Sakamoto 
195d7d20e77STakashi Sakamoto 	i = scs->output_bytes;
196d7d20e77STakashi Sakamoto 	for (;;) {
197d7d20e77STakashi Sakamoto 		if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
198d7d20e77STakashi Sakamoto 			scs->output_bytes = i;
199d7d20e77STakashi Sakamoto 			scs->output_idle = true;
200d7d20e77STakashi Sakamoto 			wake_up(&scs->idle_wait);
201d7d20e77STakashi Sakamoto 			return;
202d7d20e77STakashi Sakamoto 		}
203d7d20e77STakashi Sakamoto 		/*
204d7d20e77STakashi Sakamoto 		 * Convert from real MIDI to what I think the device expects (no
205d7d20e77STakashi Sakamoto 		 * running status, one command per packet, unescaped SysExs).
206d7d20e77STakashi Sakamoto 		 */
207d7d20e77STakashi Sakamoto 		if (scs->output_escaped && byte < 0x80) {
208d7d20e77STakashi Sakamoto 			if (scs->output_escape_high_nibble) {
209d7d20e77STakashi Sakamoto 				if (i < HSS1394_MAX_PACKET_SIZE) {
210d7d20e77STakashi Sakamoto 					scs->buffer[i] = byte << 4;
211d7d20e77STakashi Sakamoto 					scs->output_escape_high_nibble = false;
212d7d20e77STakashi Sakamoto 				}
213d7d20e77STakashi Sakamoto 			} else {
214d7d20e77STakashi Sakamoto 				scs->buffer[i++] |= byte & 0x0f;
215d7d20e77STakashi Sakamoto 				scs->output_escape_high_nibble = true;
216d7d20e77STakashi Sakamoto 			}
217d7d20e77STakashi Sakamoto 		} else if (byte < 0x80) {
218d7d20e77STakashi Sakamoto 			if (i == 1) {
219d7d20e77STakashi Sakamoto 				if (!is_valid_running_status(
220d7d20e77STakashi Sakamoto 							scs->output_status))
221d7d20e77STakashi Sakamoto 					continue;
222d7d20e77STakashi Sakamoto 				scs->buffer[0] = HSS1394_TAG_USER_DATA;
223d7d20e77STakashi Sakamoto 				scs->buffer[i++] = scs->output_status;
224d7d20e77STakashi Sakamoto 			}
225d7d20e77STakashi Sakamoto 			scs->buffer[i++] = byte;
226d7d20e77STakashi Sakamoto 			if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
227d7d20e77STakashi Sakamoto 			    (i == 4 && is_three_bytes_cmd(scs->output_status)))
228d7d20e77STakashi Sakamoto 				break;
229d7d20e77STakashi Sakamoto 			if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
230d7d20e77STakashi Sakamoto 			    !memcmp(scs->buffer + 1, sysex_escape_prefix,
231d7d20e77STakashi Sakamoto 				    ARRAY_SIZE(sysex_escape_prefix))) {
232d7d20e77STakashi Sakamoto 				scs->output_escaped = true;
233d7d20e77STakashi Sakamoto 				scs->output_escape_high_nibble = true;
234d7d20e77STakashi Sakamoto 				i = 0;
235d7d20e77STakashi Sakamoto 			}
236d7d20e77STakashi Sakamoto 			if (i >= HSS1394_MAX_PACKET_SIZE)
237d7d20e77STakashi Sakamoto 				i = 1;
238d7d20e77STakashi Sakamoto 		} else if (byte == 0xf7) {
239d7d20e77STakashi Sakamoto 			if (scs->output_escaped) {
240d7d20e77STakashi Sakamoto 				if (i >= 1 && scs->output_escape_high_nibble &&
241d7d20e77STakashi Sakamoto 				    scs->buffer[0] !=
242d7d20e77STakashi Sakamoto 						HSS1394_TAG_CHANGE_ADDRESS)
243d7d20e77STakashi Sakamoto 					break;
244d7d20e77STakashi Sakamoto 			} else {
245d7d20e77STakashi Sakamoto 				if (i > 1 && scs->output_status == 0xf0) {
246d7d20e77STakashi Sakamoto 					scs->buffer[i++] = 0xf7;
247d7d20e77STakashi Sakamoto 					break;
248d7d20e77STakashi Sakamoto 				}
249d7d20e77STakashi Sakamoto 			}
250d7d20e77STakashi Sakamoto 			i = 1;
251d7d20e77STakashi Sakamoto 			scs->output_escaped = false;
252d7d20e77STakashi Sakamoto 		} else if (!is_invalid_cmd(byte) && byte < 0xf8) {
253d7d20e77STakashi Sakamoto 			i = 1;
254d7d20e77STakashi Sakamoto 			scs->buffer[0] = HSS1394_TAG_USER_DATA;
255d7d20e77STakashi Sakamoto 			scs->buffer[i++] = byte;
256d7d20e77STakashi Sakamoto 			scs->output_status = byte;
257d7d20e77STakashi Sakamoto 			scs->output_escaped = false;
258d7d20e77STakashi Sakamoto 			if (is_one_byte_cmd(byte))
259d7d20e77STakashi Sakamoto 				break;
260d7d20e77STakashi Sakamoto 		}
261d7d20e77STakashi Sakamoto 	}
262d7d20e77STakashi Sakamoto 	scs->output_bytes = 1;
263d7d20e77STakashi Sakamoto 	scs->output_escaped = false;
264d7d20e77STakashi Sakamoto 
265b4c23ab1STakashi Sakamoto 	scs->transaction_bytes = i;
266b4c23ab1STakashi Sakamoto retry:
267d7d20e77STakashi Sakamoto 	scs->transaction_running = true;
268d7d20e77STakashi Sakamoto 	generation = scs->fw_dev->generation;
269d7d20e77STakashi Sakamoto 	smp_rmb(); /* node_id vs. generation */
270d7d20e77STakashi Sakamoto 	fw_send_request(scs->fw_dev->card, &scs->transaction,
271d7d20e77STakashi Sakamoto 			TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
272d7d20e77STakashi Sakamoto 			generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
273b4c23ab1STakashi Sakamoto 			scs->buffer, scs->transaction_bytes,
274b4c23ab1STakashi Sakamoto 			scs_write_callback, scs);
275d7d20e77STakashi Sakamoto }
276d7d20e77STakashi Sakamoto 
midi_capture_open(struct snd_rawmidi_substream * stream)2778250427dSTakashi Sakamoto static int midi_capture_open(struct snd_rawmidi_substream *stream)
2788250427dSTakashi Sakamoto {
2798250427dSTakashi Sakamoto 	return 0;
2808250427dSTakashi Sakamoto }
2818250427dSTakashi Sakamoto 
midi_capture_close(struct snd_rawmidi_substream * stream)2828250427dSTakashi Sakamoto static int midi_capture_close(struct snd_rawmidi_substream *stream)
2838250427dSTakashi Sakamoto {
2848250427dSTakashi Sakamoto 	return 0;
2858250427dSTakashi Sakamoto }
2868250427dSTakashi Sakamoto 
midi_capture_trigger(struct snd_rawmidi_substream * stream,int up)2878250427dSTakashi Sakamoto static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
2888250427dSTakashi Sakamoto {
2898250427dSTakashi Sakamoto 	struct fw_scs1x *scs = stream->rmidi->private_data;
2908250427dSTakashi Sakamoto 
2918250427dSTakashi Sakamoto 	if (up) {
2928250427dSTakashi Sakamoto 		scs->input_escape_count = 0;
2936aa7de05SMark Rutland 		WRITE_ONCE(scs->input, stream);
2948250427dSTakashi Sakamoto 	} else {
2956aa7de05SMark Rutland 		WRITE_ONCE(scs->input, NULL);
2968250427dSTakashi Sakamoto 	}
2978250427dSTakashi Sakamoto }
2988250427dSTakashi Sakamoto 
midi_playback_open(struct snd_rawmidi_substream * stream)2996f5dcb28STakashi Sakamoto static int midi_playback_open(struct snd_rawmidi_substream *stream)
3006f5dcb28STakashi Sakamoto {
3016f5dcb28STakashi Sakamoto 	return 0;
3026f5dcb28STakashi Sakamoto }
3036f5dcb28STakashi Sakamoto 
midi_playback_close(struct snd_rawmidi_substream * stream)3046f5dcb28STakashi Sakamoto static int midi_playback_close(struct snd_rawmidi_substream *stream)
3056f5dcb28STakashi Sakamoto {
3066f5dcb28STakashi Sakamoto 	return 0;
3076f5dcb28STakashi Sakamoto }
3086f5dcb28STakashi Sakamoto 
midi_playback_trigger(struct snd_rawmidi_substream * stream,int up)3096f5dcb28STakashi Sakamoto static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
3106f5dcb28STakashi Sakamoto {
3116f5dcb28STakashi Sakamoto 	struct fw_scs1x *scs = stream->rmidi->private_data;
3126f5dcb28STakashi Sakamoto 
3136f5dcb28STakashi Sakamoto 	if (up) {
3146f5dcb28STakashi Sakamoto 		scs->output_status = 0;
3156f5dcb28STakashi Sakamoto 		scs->output_bytes = 1;
3166f5dcb28STakashi Sakamoto 		scs->output_escaped = false;
3176f5dcb28STakashi Sakamoto 		scs->output_idle = false;
318b4c23ab1STakashi Sakamoto 		scs->transaction_bytes = 0;
319956dea9eSTakashi Sakamoto 		scs->error = false;
3206f5dcb28STakashi Sakamoto 
3216aa7de05SMark Rutland 		WRITE_ONCE(scs->output, stream);
322ea790053STakashi Sakamoto 		schedule_work(&scs->work);
3236f5dcb28STakashi Sakamoto 	} else {
3246aa7de05SMark Rutland 		WRITE_ONCE(scs->output, NULL);
3256f5dcb28STakashi Sakamoto 	}
3266f5dcb28STakashi Sakamoto }
midi_playback_drain(struct snd_rawmidi_substream * stream)3276f5dcb28STakashi Sakamoto static void midi_playback_drain(struct snd_rawmidi_substream *stream)
3286f5dcb28STakashi Sakamoto {
3296f5dcb28STakashi Sakamoto 	struct fw_scs1x *scs = stream->rmidi->private_data;
3306f5dcb28STakashi Sakamoto 
3316f5dcb28STakashi Sakamoto 	wait_event(scs->idle_wait, scs->output_idle);
3326f5dcb28STakashi Sakamoto }
3336f5dcb28STakashi Sakamoto 
register_address(struct snd_oxfw * oxfw)334e3315b43STakashi Sakamoto static int register_address(struct snd_oxfw *oxfw)
335e3315b43STakashi Sakamoto {
336e3315b43STakashi Sakamoto 	struct fw_scs1x *scs = oxfw->spec;
337e3315b43STakashi Sakamoto 	__be64 data;
338e3315b43STakashi Sakamoto 
339e3315b43STakashi Sakamoto 	data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
340e3315b43STakashi Sakamoto 			    scs->hss_handler.offset);
341e3315b43STakashi Sakamoto 	return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
342e3315b43STakashi Sakamoto 				  HSS1394_ADDRESS, &data, sizeof(data), 0);
343e3315b43STakashi Sakamoto }
344e3315b43STakashi Sakamoto 
remove_scs1x(struct snd_rawmidi * rmidi)345e3315b43STakashi Sakamoto static void remove_scs1x(struct snd_rawmidi *rmidi)
346e3315b43STakashi Sakamoto {
347e3315b43STakashi Sakamoto 	struct fw_scs1x *scs = rmidi->private_data;
348e3315b43STakashi Sakamoto 
349e3315b43STakashi Sakamoto 	fw_core_remove_address_handler(&scs->hss_handler);
350e3315b43STakashi Sakamoto }
351e3315b43STakashi Sakamoto 
snd_oxfw_scs1x_update(struct snd_oxfw * oxfw)352e3315b43STakashi Sakamoto void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
353e3315b43STakashi Sakamoto {
354e3315b43STakashi Sakamoto 	register_address(oxfw);
355e3315b43STakashi Sakamoto }
356e3315b43STakashi Sakamoto 
snd_oxfw_scs1x_add(struct snd_oxfw * oxfw)3573f47152aSTakashi Sakamoto int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
3583f47152aSTakashi Sakamoto {
359782fbec7STakashi Sakamoto 	static const struct snd_rawmidi_ops midi_capture_ops = {
360782fbec7STakashi Sakamoto 		.open    = midi_capture_open,
361782fbec7STakashi Sakamoto 		.close   = midi_capture_close,
362782fbec7STakashi Sakamoto 		.trigger = midi_capture_trigger,
363782fbec7STakashi Sakamoto 	};
3641753187eSTakashi Sakamoto 	static const struct snd_rawmidi_ops midi_playback_ops = {
3651753187eSTakashi Sakamoto 		.open    = midi_playback_open,
3661753187eSTakashi Sakamoto 		.close   = midi_playback_close,
3671753187eSTakashi Sakamoto 		.trigger = midi_playback_trigger,
3681753187eSTakashi Sakamoto 		.drain   = midi_playback_drain,
3691753187eSTakashi Sakamoto 	};
3703f47152aSTakashi Sakamoto 	struct snd_rawmidi *rmidi;
371e3315b43STakashi Sakamoto 	struct fw_scs1x *scs;
3723f47152aSTakashi Sakamoto 	int err;
3733f47152aSTakashi Sakamoto 
37472bc8c43STakashi Sakamoto 	scs = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_scs1x),
37572bc8c43STakashi Sakamoto 			   GFP_KERNEL);
37672bc8c43STakashi Sakamoto 	if (!scs)
377e3315b43STakashi Sakamoto 		return -ENOMEM;
378d7d20e77STakashi Sakamoto 	scs->fw_dev = fw_parent_device(oxfw->unit);
379e3315b43STakashi Sakamoto 	oxfw->spec = scs;
380e3315b43STakashi Sakamoto 
381e3315b43STakashi Sakamoto 	/* Allocate own handler for imcoming asynchronous transaction. */
382e3315b43STakashi Sakamoto 	scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
383e3315b43STakashi Sakamoto 	scs->hss_handler.address_callback = handle_hss;
384e3315b43STakashi Sakamoto 	scs->hss_handler.callback_data = scs;
385e3315b43STakashi Sakamoto 	err = fw_core_add_address_handler(&scs->hss_handler,
386e3315b43STakashi Sakamoto 					  &fw_high_memory_region);
387e3315b43STakashi Sakamoto 	if (err < 0)
388e3315b43STakashi Sakamoto 		return err;
389e3315b43STakashi Sakamoto 
390e3315b43STakashi Sakamoto 	err = register_address(oxfw);
391e3315b43STakashi Sakamoto 	if (err < 0)
392e3315b43STakashi Sakamoto 		goto err_allocated;
393e3315b43STakashi Sakamoto 
3943f47152aSTakashi Sakamoto 	/* Use unique name for backward compatibility to scs1x module. */
3956f5dcb28STakashi Sakamoto 	err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
3963f47152aSTakashi Sakamoto 	if (err < 0)
397e3315b43STakashi Sakamoto 		goto err_allocated;
398e3315b43STakashi Sakamoto 	rmidi->private_data = scs;
399e3315b43STakashi Sakamoto 	rmidi->private_free = remove_scs1x;
4003f47152aSTakashi Sakamoto 
4013f47152aSTakashi Sakamoto 	snprintf(rmidi->name, sizeof(rmidi->name),
4023f47152aSTakashi Sakamoto 		 "%s MIDI", oxfw->card->shortname);
4033f47152aSTakashi Sakamoto 
4046f5dcb28STakashi Sakamoto 	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
4056f5dcb28STakashi Sakamoto 			    SNDRV_RAWMIDI_INFO_OUTPUT |
4066f5dcb28STakashi Sakamoto 			    SNDRV_RAWMIDI_INFO_DUPLEX;
4078250427dSTakashi Sakamoto 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
4088250427dSTakashi Sakamoto 			    &midi_capture_ops);
4096f5dcb28STakashi Sakamoto 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
4106f5dcb28STakashi Sakamoto 			    &midi_playback_ops);
4118250427dSTakashi Sakamoto 
412ea790053STakashi Sakamoto 	INIT_WORK(&scs->work, scs_output_work);
413d7d20e77STakashi Sakamoto 	init_waitqueue_head(&scs->idle_wait);
414d7d20e77STakashi Sakamoto 	scs->output_idle = true;
415d7d20e77STakashi Sakamoto 
416e3315b43STakashi Sakamoto 	return 0;
417e3315b43STakashi Sakamoto err_allocated:
418e3315b43STakashi Sakamoto 	fw_core_remove_address_handler(&scs->hss_handler);
4193f47152aSTakashi Sakamoto 	return err;
4203f47152aSTakashi Sakamoto }
421