xref: /openbmc/linux/sound/firewire/oxfw/oxfw-scs1x.c (revision ea790053)
13f47152aSTakashi Sakamoto /*
23f47152aSTakashi Sakamoto  * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
33f47152aSTakashi Sakamoto  *
43f47152aSTakashi Sakamoto  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
53f47152aSTakashi Sakamoto  * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
63f47152aSTakashi Sakamoto  *
73f47152aSTakashi Sakamoto  * Licensed under the terms of the GNU General Public License, version 2.
83f47152aSTakashi Sakamoto  */
93f47152aSTakashi Sakamoto 
103f47152aSTakashi Sakamoto #include "oxfw.h"
113f47152aSTakashi Sakamoto 
12e3315b43STakashi Sakamoto #define HSS1394_ADDRESS			0xc007dedadadaULL
13e3315b43STakashi Sakamoto #define HSS1394_MAX_PACKET_SIZE		64
1413b8b78cSTakashi Sakamoto #define HSS1394_TAG_USER_DATA		0x00
15e3315b43STakashi Sakamoto #define HSS1394_TAG_CHANGE_ADDRESS	0xf1
16e3315b43STakashi Sakamoto 
17e3315b43STakashi Sakamoto struct fw_scs1x {
18e3315b43STakashi Sakamoto 	struct fw_address_handler hss_handler;
1913b8b78cSTakashi Sakamoto 	u8 input_escape_count;
2013b8b78cSTakashi Sakamoto 	struct snd_rawmidi_substream *input;
21d7d20e77STakashi Sakamoto 
22d7d20e77STakashi Sakamoto 	/* For MIDI playback. */
23d7d20e77STakashi Sakamoto 	struct snd_rawmidi_substream *output;
24d7d20e77STakashi Sakamoto 	bool output_idle;
25d7d20e77STakashi Sakamoto 	u8 output_status;
26d7d20e77STakashi Sakamoto 	u8 output_bytes;
27d7d20e77STakashi Sakamoto 	bool output_escaped;
28d7d20e77STakashi Sakamoto 	bool output_escape_high_nibble;
29ea790053STakashi Sakamoto 	struct work_struct work;
30d7d20e77STakashi Sakamoto 	wait_queue_head_t idle_wait;
31d7d20e77STakashi Sakamoto 	u8 buffer[HSS1394_MAX_PACKET_SIZE];
32d7d20e77STakashi Sakamoto 	bool transaction_running;
33d7d20e77STakashi Sakamoto 	struct fw_transaction transaction;
34d7d20e77STakashi Sakamoto 	struct fw_device *fw_dev;
35e3315b43STakashi Sakamoto };
36e3315b43STakashi Sakamoto 
3713b8b78cSTakashi Sakamoto static const u8 sysex_escape_prefix[] = {
3813b8b78cSTakashi Sakamoto 	0xf0,			/* SysEx begin */
3913b8b78cSTakashi Sakamoto 	0x00, 0x01, 0x60,	/* Stanton DJ */
4013b8b78cSTakashi Sakamoto 	0x48, 0x53, 0x53,	/* "HSS" */
4113b8b78cSTakashi Sakamoto };
4213b8b78cSTakashi Sakamoto 
4313b8b78cSTakashi Sakamoto static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
4413b8b78cSTakashi Sakamoto 				    u8 byte)
4513b8b78cSTakashi Sakamoto {
4613b8b78cSTakashi Sakamoto 	u8 nibbles[2];
4713b8b78cSTakashi Sakamoto 
4813b8b78cSTakashi Sakamoto 	nibbles[0] = byte >> 4;
4913b8b78cSTakashi Sakamoto 	nibbles[1] = byte & 0x0f;
5013b8b78cSTakashi Sakamoto 	snd_rawmidi_receive(stream, nibbles, 2);
5113b8b78cSTakashi Sakamoto }
5213b8b78cSTakashi Sakamoto 
5313b8b78cSTakashi Sakamoto static void midi_input_byte(struct fw_scs1x *scs,
5413b8b78cSTakashi Sakamoto 			    struct snd_rawmidi_substream *stream, u8 byte)
5513b8b78cSTakashi Sakamoto {
5613b8b78cSTakashi Sakamoto 	const u8 eox = 0xf7;
5713b8b78cSTakashi Sakamoto 
5813b8b78cSTakashi Sakamoto 	if (scs->input_escape_count > 0) {
5913b8b78cSTakashi Sakamoto 		midi_input_escaped_byte(stream, byte);
6013b8b78cSTakashi Sakamoto 		scs->input_escape_count--;
6113b8b78cSTakashi Sakamoto 		if (scs->input_escape_count == 0)
6213b8b78cSTakashi Sakamoto 			snd_rawmidi_receive(stream, &eox, sizeof(eox));
6313b8b78cSTakashi Sakamoto 	} else if (byte == 0xf9) {
6413b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, sysex_escape_prefix,
6513b8b78cSTakashi Sakamoto 				    ARRAY_SIZE(sysex_escape_prefix));
6613b8b78cSTakashi Sakamoto 		midi_input_escaped_byte(stream, 0x00);
6713b8b78cSTakashi Sakamoto 		midi_input_escaped_byte(stream, 0xf9);
6813b8b78cSTakashi Sakamoto 		scs->input_escape_count = 3;
6913b8b78cSTakashi Sakamoto 	} else {
7013b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, &byte, 1);
7113b8b78cSTakashi Sakamoto 	}
7213b8b78cSTakashi Sakamoto }
7313b8b78cSTakashi Sakamoto 
7413b8b78cSTakashi Sakamoto static void midi_input_packet(struct fw_scs1x *scs,
7513b8b78cSTakashi Sakamoto 			      struct snd_rawmidi_substream *stream,
7613b8b78cSTakashi Sakamoto 			      const u8 *data, unsigned int bytes)
7713b8b78cSTakashi Sakamoto {
7813b8b78cSTakashi Sakamoto 	unsigned int i;
7913b8b78cSTakashi Sakamoto 	const u8 eox = 0xf7;
8013b8b78cSTakashi Sakamoto 
8113b8b78cSTakashi Sakamoto 	if (data[0] == HSS1394_TAG_USER_DATA) {
8213b8b78cSTakashi Sakamoto 		for (i = 1; i < bytes; ++i)
8313b8b78cSTakashi Sakamoto 			midi_input_byte(scs, stream, data[i]);
8413b8b78cSTakashi Sakamoto 	} else {
8513b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, sysex_escape_prefix,
8613b8b78cSTakashi Sakamoto 				    ARRAY_SIZE(sysex_escape_prefix));
8713b8b78cSTakashi Sakamoto 		for (i = 0; i < bytes; ++i)
8813b8b78cSTakashi Sakamoto 			midi_input_escaped_byte(stream, data[i]);
8913b8b78cSTakashi Sakamoto 		snd_rawmidi_receive(stream, &eox, sizeof(eox));
9013b8b78cSTakashi Sakamoto 	}
9113b8b78cSTakashi Sakamoto }
9213b8b78cSTakashi Sakamoto 
93e3315b43STakashi Sakamoto static void handle_hss(struct fw_card *card, struct fw_request *request,
94e3315b43STakashi Sakamoto 		       int tcode, int destination, int source, int generation,
95e3315b43STakashi Sakamoto 		       unsigned long long offset, void *data, size_t length,
96e3315b43STakashi Sakamoto 		       void *callback_data)
97e3315b43STakashi Sakamoto {
9813b8b78cSTakashi Sakamoto 	struct fw_scs1x *scs = callback_data;
9913b8b78cSTakashi Sakamoto 	struct snd_rawmidi_substream *stream;
10013b8b78cSTakashi Sakamoto 	int rcode;
10113b8b78cSTakashi Sakamoto 
10213b8b78cSTakashi Sakamoto 	if (offset != scs->hss_handler.offset) {
10313b8b78cSTakashi Sakamoto 		rcode = RCODE_ADDRESS_ERROR;
10413b8b78cSTakashi Sakamoto 		goto end;
10513b8b78cSTakashi Sakamoto 	}
10613b8b78cSTakashi Sakamoto 	if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
10713b8b78cSTakashi Sakamoto 	    tcode != TCODE_WRITE_BLOCK_REQUEST) {
10813b8b78cSTakashi Sakamoto 		rcode = RCODE_TYPE_ERROR;
10913b8b78cSTakashi Sakamoto 		goto end;
11013b8b78cSTakashi Sakamoto 	}
11113b8b78cSTakashi Sakamoto 
11213b8b78cSTakashi Sakamoto 	if (length >= 1) {
11313b8b78cSTakashi Sakamoto 		stream = ACCESS_ONCE(scs->input);
11413b8b78cSTakashi Sakamoto 		if (stream)
11513b8b78cSTakashi Sakamoto 			midi_input_packet(scs, stream, data, length);
11613b8b78cSTakashi Sakamoto 	}
11713b8b78cSTakashi Sakamoto 
11813b8b78cSTakashi Sakamoto 	rcode = RCODE_COMPLETE;
11913b8b78cSTakashi Sakamoto end:
12013b8b78cSTakashi Sakamoto 	fw_send_response(card, request, rcode);
121e3315b43STakashi Sakamoto }
122e3315b43STakashi Sakamoto 
123d7d20e77STakashi Sakamoto static void scs_write_callback(struct fw_card *card, int rcode,
124d7d20e77STakashi Sakamoto 			       void *data, size_t length, void *callback_data)
125d7d20e77STakashi Sakamoto {
126d7d20e77STakashi Sakamoto 	struct fw_scs1x *scs = callback_data;
127d7d20e77STakashi Sakamoto 
128d7d20e77STakashi Sakamoto 	if (rcode == RCODE_GENERATION)
129d7d20e77STakashi Sakamoto 		;	/* TODO: retry this packet */
130d7d20e77STakashi Sakamoto 
131d7d20e77STakashi Sakamoto 	scs->transaction_running = false;
132ea790053STakashi Sakamoto 	schedule_work(&scs->work);
133d7d20e77STakashi Sakamoto }
134d7d20e77STakashi Sakamoto 
135d7d20e77STakashi Sakamoto static bool is_valid_running_status(u8 status)
136d7d20e77STakashi Sakamoto {
137d7d20e77STakashi Sakamoto 	return status >= 0x80 && status <= 0xef;
138d7d20e77STakashi Sakamoto }
139d7d20e77STakashi Sakamoto 
140d7d20e77STakashi Sakamoto static bool is_one_byte_cmd(u8 status)
141d7d20e77STakashi Sakamoto {
142d7d20e77STakashi Sakamoto 	return status == 0xf6 ||
143d7d20e77STakashi Sakamoto 	       status >= 0xf8;
144d7d20e77STakashi Sakamoto }
145d7d20e77STakashi Sakamoto 
146d7d20e77STakashi Sakamoto static bool is_two_bytes_cmd(u8 status)
147d7d20e77STakashi Sakamoto {
148d7d20e77STakashi Sakamoto 	return (status >= 0xc0 && status <= 0xdf) ||
149d7d20e77STakashi Sakamoto 	       status == 0xf1 ||
150d7d20e77STakashi Sakamoto 	       status == 0xf3;
151d7d20e77STakashi Sakamoto }
152d7d20e77STakashi Sakamoto 
153d7d20e77STakashi Sakamoto static bool is_three_bytes_cmd(u8 status)
154d7d20e77STakashi Sakamoto {
155d7d20e77STakashi Sakamoto 	return (status >= 0x80 && status <= 0xbf) ||
156d7d20e77STakashi Sakamoto 	       (status >= 0xe0 && status <= 0xef) ||
157d7d20e77STakashi Sakamoto 	       status == 0xf2;
158d7d20e77STakashi Sakamoto }
159d7d20e77STakashi Sakamoto 
160d7d20e77STakashi Sakamoto static bool is_invalid_cmd(u8 status)
161d7d20e77STakashi Sakamoto {
162d7d20e77STakashi Sakamoto 	return status == 0xf4 ||
163d7d20e77STakashi Sakamoto 	       status == 0xf5 ||
164d7d20e77STakashi Sakamoto 	       status == 0xf9 ||
165d7d20e77STakashi Sakamoto 	       status == 0xfd;
166d7d20e77STakashi Sakamoto }
167d7d20e77STakashi Sakamoto 
168ea790053STakashi Sakamoto static void scs_output_work(struct work_struct *work)
169d7d20e77STakashi Sakamoto {
170ea790053STakashi Sakamoto 	struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
171d7d20e77STakashi Sakamoto 	struct snd_rawmidi_substream *stream;
172d7d20e77STakashi Sakamoto 	unsigned int i;
173d7d20e77STakashi Sakamoto 	u8 byte;
174d7d20e77STakashi Sakamoto 	int generation;
175d7d20e77STakashi Sakamoto 
176d7d20e77STakashi Sakamoto 	if (scs->transaction_running)
177d7d20e77STakashi Sakamoto 		return;
178d7d20e77STakashi Sakamoto 
179d7d20e77STakashi Sakamoto 	stream = ACCESS_ONCE(scs->output);
180d7d20e77STakashi Sakamoto 	if (!stream) {
181d7d20e77STakashi Sakamoto 		scs->output_idle = true;
182d7d20e77STakashi Sakamoto 		wake_up(&scs->idle_wait);
183d7d20e77STakashi Sakamoto 		return;
184d7d20e77STakashi Sakamoto 	}
185d7d20e77STakashi Sakamoto 
186d7d20e77STakashi Sakamoto 	i = scs->output_bytes;
187d7d20e77STakashi Sakamoto 	for (;;) {
188d7d20e77STakashi Sakamoto 		if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
189d7d20e77STakashi Sakamoto 			scs->output_bytes = i;
190d7d20e77STakashi Sakamoto 			scs->output_idle = true;
191d7d20e77STakashi Sakamoto 			wake_up(&scs->idle_wait);
192d7d20e77STakashi Sakamoto 			return;
193d7d20e77STakashi Sakamoto 		}
194d7d20e77STakashi Sakamoto 		/*
195d7d20e77STakashi Sakamoto 		 * Convert from real MIDI to what I think the device expects (no
196d7d20e77STakashi Sakamoto 		 * running status, one command per packet, unescaped SysExs).
197d7d20e77STakashi Sakamoto 		 */
198d7d20e77STakashi Sakamoto 		if (scs->output_escaped && byte < 0x80) {
199d7d20e77STakashi Sakamoto 			if (scs->output_escape_high_nibble) {
200d7d20e77STakashi Sakamoto 				if (i < HSS1394_MAX_PACKET_SIZE) {
201d7d20e77STakashi Sakamoto 					scs->buffer[i] = byte << 4;
202d7d20e77STakashi Sakamoto 					scs->output_escape_high_nibble = false;
203d7d20e77STakashi Sakamoto 				}
204d7d20e77STakashi Sakamoto 			} else {
205d7d20e77STakashi Sakamoto 				scs->buffer[i++] |= byte & 0x0f;
206d7d20e77STakashi Sakamoto 				scs->output_escape_high_nibble = true;
207d7d20e77STakashi Sakamoto 			}
208d7d20e77STakashi Sakamoto 		} else if (byte < 0x80) {
209d7d20e77STakashi Sakamoto 			if (i == 1) {
210d7d20e77STakashi Sakamoto 				if (!is_valid_running_status(
211d7d20e77STakashi Sakamoto 							scs->output_status))
212d7d20e77STakashi Sakamoto 					continue;
213d7d20e77STakashi Sakamoto 				scs->buffer[0] = HSS1394_TAG_USER_DATA;
214d7d20e77STakashi Sakamoto 				scs->buffer[i++] = scs->output_status;
215d7d20e77STakashi Sakamoto 			}
216d7d20e77STakashi Sakamoto 			scs->buffer[i++] = byte;
217d7d20e77STakashi Sakamoto 			if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
218d7d20e77STakashi Sakamoto 			    (i == 4 && is_three_bytes_cmd(scs->output_status)))
219d7d20e77STakashi Sakamoto 				break;
220d7d20e77STakashi Sakamoto 			if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
221d7d20e77STakashi Sakamoto 			    !memcmp(scs->buffer + 1, sysex_escape_prefix,
222d7d20e77STakashi Sakamoto 				    ARRAY_SIZE(sysex_escape_prefix))) {
223d7d20e77STakashi Sakamoto 				scs->output_escaped = true;
224d7d20e77STakashi Sakamoto 				scs->output_escape_high_nibble = true;
225d7d20e77STakashi Sakamoto 				i = 0;
226d7d20e77STakashi Sakamoto 			}
227d7d20e77STakashi Sakamoto 			if (i >= HSS1394_MAX_PACKET_SIZE)
228d7d20e77STakashi Sakamoto 				i = 1;
229d7d20e77STakashi Sakamoto 		} else if (byte == 0xf7) {
230d7d20e77STakashi Sakamoto 			if (scs->output_escaped) {
231d7d20e77STakashi Sakamoto 				if (i >= 1 && scs->output_escape_high_nibble &&
232d7d20e77STakashi Sakamoto 				    scs->buffer[0] !=
233d7d20e77STakashi Sakamoto 						HSS1394_TAG_CHANGE_ADDRESS)
234d7d20e77STakashi Sakamoto 					break;
235d7d20e77STakashi Sakamoto 			} else {
236d7d20e77STakashi Sakamoto 				if (i > 1 && scs->output_status == 0xf0) {
237d7d20e77STakashi Sakamoto 					scs->buffer[i++] = 0xf7;
238d7d20e77STakashi Sakamoto 					break;
239d7d20e77STakashi Sakamoto 				}
240d7d20e77STakashi Sakamoto 			}
241d7d20e77STakashi Sakamoto 			i = 1;
242d7d20e77STakashi Sakamoto 			scs->output_escaped = false;
243d7d20e77STakashi Sakamoto 		} else if (!is_invalid_cmd(byte) && byte < 0xf8) {
244d7d20e77STakashi Sakamoto 			i = 1;
245d7d20e77STakashi Sakamoto 			scs->buffer[0] = HSS1394_TAG_USER_DATA;
246d7d20e77STakashi Sakamoto 			scs->buffer[i++] = byte;
247d7d20e77STakashi Sakamoto 			scs->output_status = byte;
248d7d20e77STakashi Sakamoto 			scs->output_escaped = false;
249d7d20e77STakashi Sakamoto 			if (is_one_byte_cmd(byte))
250d7d20e77STakashi Sakamoto 				break;
251d7d20e77STakashi Sakamoto 		}
252d7d20e77STakashi Sakamoto 	}
253d7d20e77STakashi Sakamoto 	scs->output_bytes = 1;
254d7d20e77STakashi Sakamoto 	scs->output_escaped = false;
255d7d20e77STakashi Sakamoto 
256d7d20e77STakashi Sakamoto 	scs->transaction_running = true;
257d7d20e77STakashi Sakamoto 	generation = scs->fw_dev->generation;
258d7d20e77STakashi Sakamoto 	smp_rmb(); /* node_id vs. generation */
259d7d20e77STakashi Sakamoto 	fw_send_request(scs->fw_dev->card, &scs->transaction,
260d7d20e77STakashi Sakamoto 			TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
261d7d20e77STakashi Sakamoto 			generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
262d7d20e77STakashi Sakamoto 			scs->buffer, i, scs_write_callback, scs);
263d7d20e77STakashi Sakamoto }
264d7d20e77STakashi Sakamoto 
2658250427dSTakashi Sakamoto static int midi_capture_open(struct snd_rawmidi_substream *stream)
2668250427dSTakashi Sakamoto {
2678250427dSTakashi Sakamoto 	return 0;
2688250427dSTakashi Sakamoto }
2698250427dSTakashi Sakamoto 
2708250427dSTakashi Sakamoto static int midi_capture_close(struct snd_rawmidi_substream *stream)
2718250427dSTakashi Sakamoto {
2728250427dSTakashi Sakamoto 	return 0;
2738250427dSTakashi Sakamoto }
2748250427dSTakashi Sakamoto 
2758250427dSTakashi Sakamoto static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
2768250427dSTakashi Sakamoto {
2778250427dSTakashi Sakamoto 	struct fw_scs1x *scs = stream->rmidi->private_data;
2788250427dSTakashi Sakamoto 
2798250427dSTakashi Sakamoto 	if (up) {
2808250427dSTakashi Sakamoto 		scs->input_escape_count = 0;
2818250427dSTakashi Sakamoto 		ACCESS_ONCE(scs->input) = stream;
2828250427dSTakashi Sakamoto 	} else {
2838250427dSTakashi Sakamoto 		ACCESS_ONCE(scs->input) = NULL;
2848250427dSTakashi Sakamoto 	}
2858250427dSTakashi Sakamoto }
2868250427dSTakashi Sakamoto 
2878250427dSTakashi Sakamoto static struct snd_rawmidi_ops midi_capture_ops = {
2888250427dSTakashi Sakamoto 	.open    = midi_capture_open,
2898250427dSTakashi Sakamoto 	.close   = midi_capture_close,
2908250427dSTakashi Sakamoto 	.trigger = midi_capture_trigger,
2918250427dSTakashi Sakamoto };
2928250427dSTakashi Sakamoto 
2936f5dcb28STakashi Sakamoto static int midi_playback_open(struct snd_rawmidi_substream *stream)
2946f5dcb28STakashi Sakamoto {
2956f5dcb28STakashi Sakamoto 	return 0;
2966f5dcb28STakashi Sakamoto }
2976f5dcb28STakashi Sakamoto 
2986f5dcb28STakashi Sakamoto static int midi_playback_close(struct snd_rawmidi_substream *stream)
2996f5dcb28STakashi Sakamoto {
3006f5dcb28STakashi Sakamoto 	return 0;
3016f5dcb28STakashi Sakamoto }
3026f5dcb28STakashi Sakamoto 
3036f5dcb28STakashi Sakamoto static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
3046f5dcb28STakashi Sakamoto {
3056f5dcb28STakashi Sakamoto 	struct fw_scs1x *scs = stream->rmidi->private_data;
3066f5dcb28STakashi Sakamoto 
3076f5dcb28STakashi Sakamoto 	if (up) {
3086f5dcb28STakashi Sakamoto 		scs->output_status = 0;
3096f5dcb28STakashi Sakamoto 		scs->output_bytes = 1;
3106f5dcb28STakashi Sakamoto 		scs->output_escaped = false;
3116f5dcb28STakashi Sakamoto 		scs->output_idle = false;
3126f5dcb28STakashi Sakamoto 
3136f5dcb28STakashi Sakamoto 		ACCESS_ONCE(scs->output) = stream;
314ea790053STakashi Sakamoto 		schedule_work(&scs->work);
3156f5dcb28STakashi Sakamoto 	} else {
3166f5dcb28STakashi Sakamoto 		ACCESS_ONCE(scs->output) = NULL;
3176f5dcb28STakashi Sakamoto 	}
3186f5dcb28STakashi Sakamoto }
3196f5dcb28STakashi Sakamoto static void midi_playback_drain(struct snd_rawmidi_substream *stream)
3206f5dcb28STakashi Sakamoto {
3216f5dcb28STakashi Sakamoto 	struct fw_scs1x *scs = stream->rmidi->private_data;
3226f5dcb28STakashi Sakamoto 
3236f5dcb28STakashi Sakamoto 	wait_event(scs->idle_wait, scs->output_idle);
3246f5dcb28STakashi Sakamoto }
3256f5dcb28STakashi Sakamoto 
3266f5dcb28STakashi Sakamoto static struct snd_rawmidi_ops midi_playback_ops = {
3276f5dcb28STakashi Sakamoto 	.open    = midi_playback_open,
3286f5dcb28STakashi Sakamoto 	.close   = midi_playback_close,
3296f5dcb28STakashi Sakamoto 	.trigger = midi_playback_trigger,
3306f5dcb28STakashi Sakamoto 	.drain   = midi_playback_drain,
3316f5dcb28STakashi Sakamoto };
332e3315b43STakashi Sakamoto static int register_address(struct snd_oxfw *oxfw)
333e3315b43STakashi Sakamoto {
334e3315b43STakashi Sakamoto 	struct fw_scs1x *scs = oxfw->spec;
335e3315b43STakashi Sakamoto 	__be64 data;
336e3315b43STakashi Sakamoto 
337e3315b43STakashi Sakamoto 	data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
338e3315b43STakashi Sakamoto 			    scs->hss_handler.offset);
339e3315b43STakashi Sakamoto 	return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
340e3315b43STakashi Sakamoto 				  HSS1394_ADDRESS, &data, sizeof(data), 0);
341e3315b43STakashi Sakamoto }
342e3315b43STakashi Sakamoto 
343e3315b43STakashi Sakamoto static void remove_scs1x(struct snd_rawmidi *rmidi)
344e3315b43STakashi Sakamoto {
345e3315b43STakashi Sakamoto 	struct fw_scs1x *scs = rmidi->private_data;
346e3315b43STakashi Sakamoto 
347e3315b43STakashi Sakamoto 	fw_core_remove_address_handler(&scs->hss_handler);
348e3315b43STakashi Sakamoto }
349e3315b43STakashi Sakamoto 
350e3315b43STakashi Sakamoto void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
351e3315b43STakashi Sakamoto {
352e3315b43STakashi Sakamoto 	register_address(oxfw);
353e3315b43STakashi Sakamoto }
354e3315b43STakashi Sakamoto 
3553f47152aSTakashi Sakamoto int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
3563f47152aSTakashi Sakamoto {
3573f47152aSTakashi Sakamoto 	struct snd_rawmidi *rmidi;
358e3315b43STakashi Sakamoto 	struct fw_scs1x *scs;
3593f47152aSTakashi Sakamoto 	int err;
3603f47152aSTakashi Sakamoto 
361e3315b43STakashi Sakamoto 	scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL);
362e3315b43STakashi Sakamoto 	if (scs == NULL)
363e3315b43STakashi Sakamoto 		return -ENOMEM;
364d7d20e77STakashi Sakamoto 	scs->fw_dev = fw_parent_device(oxfw->unit);
365e3315b43STakashi Sakamoto 	oxfw->spec = scs;
366e3315b43STakashi Sakamoto 
367e3315b43STakashi Sakamoto 	/* Allocate own handler for imcoming asynchronous transaction. */
368e3315b43STakashi Sakamoto 	scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
369e3315b43STakashi Sakamoto 	scs->hss_handler.address_callback = handle_hss;
370e3315b43STakashi Sakamoto 	scs->hss_handler.callback_data = scs;
371e3315b43STakashi Sakamoto 	err = fw_core_add_address_handler(&scs->hss_handler,
372e3315b43STakashi Sakamoto 					  &fw_high_memory_region);
373e3315b43STakashi Sakamoto 	if (err < 0)
374e3315b43STakashi Sakamoto 		return err;
375e3315b43STakashi Sakamoto 
376e3315b43STakashi Sakamoto 	err = register_address(oxfw);
377e3315b43STakashi Sakamoto 	if (err < 0)
378e3315b43STakashi Sakamoto 		goto err_allocated;
379e3315b43STakashi Sakamoto 
3803f47152aSTakashi Sakamoto 	/* Use unique name for backward compatibility to scs1x module. */
3816f5dcb28STakashi Sakamoto 	err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
3823f47152aSTakashi Sakamoto 	if (err < 0)
383e3315b43STakashi Sakamoto 		goto err_allocated;
384e3315b43STakashi Sakamoto 	rmidi->private_data = scs;
385e3315b43STakashi Sakamoto 	rmidi->private_free = remove_scs1x;
3863f47152aSTakashi Sakamoto 
3873f47152aSTakashi Sakamoto 	snprintf(rmidi->name, sizeof(rmidi->name),
3883f47152aSTakashi Sakamoto 		 "%s MIDI", oxfw->card->shortname);
3893f47152aSTakashi Sakamoto 
3906f5dcb28STakashi Sakamoto 	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
3916f5dcb28STakashi Sakamoto 			    SNDRV_RAWMIDI_INFO_OUTPUT |
3926f5dcb28STakashi Sakamoto 			    SNDRV_RAWMIDI_INFO_DUPLEX;
3938250427dSTakashi Sakamoto 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
3948250427dSTakashi Sakamoto 			    &midi_capture_ops);
3956f5dcb28STakashi Sakamoto 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
3966f5dcb28STakashi Sakamoto 			    &midi_playback_ops);
3978250427dSTakashi Sakamoto 
398ea790053STakashi Sakamoto 	INIT_WORK(&scs->work, scs_output_work);
399d7d20e77STakashi Sakamoto 	init_waitqueue_head(&scs->idle_wait);
400d7d20e77STakashi Sakamoto 	scs->output_idle = true;
401d7d20e77STakashi Sakamoto 
402e3315b43STakashi Sakamoto 	return 0;
403e3315b43STakashi Sakamoto err_allocated:
404e3315b43STakashi Sakamoto 	fw_core_remove_address_handler(&scs->hss_handler);
4053f47152aSTakashi Sakamoto 	return err;
4063f47152aSTakashi Sakamoto }
407