1*da607e19SThomas 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