diff options
Diffstat (limited to 'sound/firewire')
23 files changed, 1163 insertions, 758 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 052e00590259..b9e96d0b3a0a 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -163,5 +163,6 @@ config SND_FIREFACE Say Y here to include support for RME fireface series. * Fireface 400 * Fireface 800 + * Fireface UCX endif # SND_FIREWIRE diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index d91874275d2c..5b46e8dcc2dd 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -448,7 +448,19 @@ static const struct ieee1394_device_id bebob_id_table[] = { /* Focusrite, SaffirePro 26 I/O */ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec), /* Focusrite, SaffirePro 10 I/O */ - SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec), + { + // The combination of vendor_id and model_id is the same as the + // same as the one of Liquid Saffire 56. + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VEN_FOCUSRITE, + .model_id = 0x000006, + .specifier_id = 0x00a02d, + .version = 0x010001, + .driver_data = (kernel_ulong_t)&saffirepro_10_spec, + }, /* Focusrite, Saffire(no label and LE) */ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH, &saffire_spec), diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c index 8096891af913..05e2a1c6326c 100644 --- a/sound/firewire/bebob/bebob_proc.c +++ b/sound/firewire/bebob/bebob_proc.c @@ -163,12 +163,8 @@ add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name, struct snd_info_entry *entry; entry = snd_info_create_card_entry(bebob->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, bebob, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, bebob, op); } void snd_bebob_proc_init(struct snd_bebob *bebob) @@ -184,10 +180,6 @@ void snd_bebob_proc_init(struct snd_bebob *bebob) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(bebob, root, "clock", proc_read_clock); add_node(bebob, root, "firmware", proc_read_hw_info); diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c index bb870fc73f99..9b1d509c6320 100644 --- a/sound/firewire/dice/dice-proc.c +++ b/sound/firewire/dice/dice-proc.c @@ -285,12 +285,8 @@ static void add_node(struct snd_dice *dice, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(dice->card, name, root); - if (!entry) - return; - - snd_info_set_text_ops(entry, dice, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, dice, op); } void snd_dice_create_proc(struct snd_dice *dice) @@ -306,10 +302,6 @@ void snd_dice_create_proc(struct snd_dice *dice) if (!root) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(dice, root, "dice", dice_proc_read); add_node(dice, root, "formation", dice_proc_read_formation); diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index ed50b222d36e..eee184b05d93 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -18,6 +18,7 @@ MODULE_LICENSE("GPL v2"); #define OUI_ALESIS 0x000595 #define OUI_MAUDIO 0x000d6c #define OUI_MYTEK 0x001ee8 +#define OUI_SSL 0x0050c2 // Actually ID reserved by IEEE. #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 @@ -196,7 +197,7 @@ static int dice_probe(struct fw_unit *unit, struct snd_dice *dice; int err; - if (!entry->driver_data) { + if (!entry->driver_data && entry->vendor_id != OUI_SSL) { err = check_dice_category(unit); if (err < 0) return -ENODEV; @@ -361,6 +362,15 @@ static const struct ieee1394_device_id dice_id_table[] = { .model_id = 0x000002, .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats, }, + // Solid State Logic, Duende Classic and Mini. + // NOTE: each field of GUID in config ROM is not compliant to standard + // DICE scheme. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_SSL, + .model_id = 0x000070, + }, { .match_flags = IEEE1394_MATCH_VERSION, .version = DICE_INTERFACE, diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c index 6996d5a6ff5f..d22e8675b10f 100644 --- a/sound/firewire/digi00x/digi00x-proc.c +++ b/sound/firewire/digi00x/digi00x-proc.c @@ -80,20 +80,8 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } entry = snd_info_create_card_entry(dg00x->card, "clock", root); - if (entry == NULL) { - snd_info_free_entry(root); - return; - } - - snd_info_set_text_ops(entry, dg00x, proc_read_clock); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - snd_info_free_entry(root); - } + if (entry) + snd_info_set_text_ops(entry, dg00x, proc_read_clock); } diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 79a7d6d99d72..d64f4e2a1096 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,4 +1,4 @@ snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ - ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o \ - ff-protocol-ff800.o + ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \ + ff-protocol-latter.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c index 6a49611ee462..5b44e1c4569a 100644 --- a/sound/firewire/fireface/ff-midi.c +++ b/sound/firewire/fireface/ff-midi.c @@ -19,7 +19,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) struct snd_ff *ff = substream->rmidi->private_data; /* Initialize internal status. */ - ff->running_status[substream->number] = 0; + ff->on_sysex[substream->number] = 0; ff->rx_midi_error[substream->number] = false; WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream); diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index d0bc96b20a65..5adf04b95c04 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -152,7 +152,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock; - err = snd_ff_transaction_get_clock(ff, &rate, &src); + err = ff->spec->protocol->get_clock(ff, &rate, &src); if (err < 0) goto release_lock; diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index a0c550dabe9a..b886b541c94b 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -8,209 +8,29 @@ #include "./ff.h" -static void proc_dump_clock_config(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src) { - struct snd_ff *ff = entry->private_data; - __le32 reg; - u32 data; - unsigned int rate; - const char *src; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, - SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", - (data & 0x20) ? "Professional" : "Consumer", - (data & 0x40) ? "on" : "off"); - - snd_iprintf(buffer, "Optical output interface format: %s\n", - ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); - - snd_iprintf(buffer, "Word output single speed: %s\n", - ((data >> 8) & 0x20) ? "on" : "off"); - - snd_iprintf(buffer, "S/PDIF input interface: %s\n", - ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); - - switch ((data >> 1) & 0x03) { - case 0x01: - rate = 32000; - break; - case 0x00: - rate = 44100; - break; - case 0x03: - rate = 48000; - break; - case 0x02: - default: - return; - } - - if (data & 0x08) - rate *= 2; - else if (data & 0x10) - rate *= 4; - - snd_iprintf(buffer, "Sampling rate: %d\n", rate); - - if (data & 0x01) { - src = "Internal"; - } else { - switch ((data >> 10) & 0x07) { - case 0x00: - src = "ADAT1"; - break; - case 0x01: - src = "ADAT2"; - break; - case 0x03: - src = "S/PDIF"; - break; - case 0x04: - src = "Word"; - break; - case 0x05: - src = "LTC"; - break; - default: - return; - } - } - - snd_iprintf(buffer, "Sync to clock source: %s\n", src); + static const char *const labels[] = { + "Internal", + "S/PDIF", + "ADAT1", + "ADAT2", + "Word", + "LTC", + }; + + if (src >= ARRAY_SIZE(labels)) + return NULL; + + return labels[src]; } -static void proc_dump_sync_status(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +static void proc_dump_status(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; - __le32 reg; - u32 data; - int err; - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "External source detection:\n"); - - snd_iprintf(buffer, "Word Clock:"); - if ((data >> 24) & 0x20) { - if ((data >> 24) & 0x40) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "S/PDIF:"); - if ((data >> 16) & 0x10) { - if ((data >> 16) & 0x04) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "ADAT1:"); - if ((data >> 8) & 0x04) { - if ((data >> 8) & 0x10) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "ADAT2:"); - if ((data >> 8) & 0x08) { - if ((data >> 8) & 0x20) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "\nUsed external source:\n"); - - if (((data >> 22) & 0x07) == 0x07) { - snd_iprintf(buffer, "None\n"); - } else { - switch ((data >> 22) & 0x07) { - case 0x00: - snd_iprintf(buffer, "ADAT1:"); - break; - case 0x01: - snd_iprintf(buffer, "ADAT2:"); - break; - case 0x03: - snd_iprintf(buffer, "S/PDIF:"); - break; - case 0x04: - snd_iprintf(buffer, "Word:"); - break; - case 0x07: - snd_iprintf(buffer, "Nothing:"); - break; - case 0x02: - case 0x05: - case 0x06: - default: - snd_iprintf(buffer, "unknown:"); - break; - } - - if ((data >> 25) & 0x07) { - switch ((data >> 25) & 0x07) { - case 0x01: - snd_iprintf(buffer, "32000\n"); - break; - case 0x02: - snd_iprintf(buffer, "44100\n"); - break; - case 0x03: - snd_iprintf(buffer, "48000\n"); - break; - case 0x04: - snd_iprintf(buffer, "64000\n"); - break; - case 0x05: - snd_iprintf(buffer, "88200\n"); - break; - case 0x06: - snd_iprintf(buffer, "96000\n"); - break; - case 0x07: - snd_iprintf(buffer, "128000\n"); - break; - case 0x08: - snd_iprintf(buffer, "176400\n"); - break; - case 0x09: - snd_iprintf(buffer, "192000\n"); - break; - case 0x00: - snd_iprintf(buffer, "unknown\n"); - break; - } - } - } - - snd_iprintf(buffer, "Multiplied:"); - snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); + ff->spec->protocol->dump_status(ff, buffer); } static void add_node(struct snd_ff *ff, struct snd_info_entry *root, @@ -221,12 +41,8 @@ static void add_node(struct snd_ff *ff, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(ff->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, ff, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, ff, op); } void snd_ff_proc_init(struct snd_ff *ff) @@ -242,11 +58,6 @@ void snd_ff_proc_init(struct snd_ff *ff) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } - add_node(ff, root, "clock-config", proc_dump_clock_config); - add_node(ff, root, "sync-status", proc_dump_sync_status); + add_node(ff, root, "status", proc_dump_status); } diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c deleted file mode 100644 index 2280fab9b3c7..000000000000 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * ff-protocol-ff400.c - a part of driver for RME Fireface series - * - * Copyright (c) 2015-2017 Takashi Sakamoto - * - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include <linux/delay.h> -#include "ff.h" - -#define FF400_STF 0x000080100500ull -#define FF400_RX_PACKET_FORMAT 0x000080100504ull -#define FF400_ISOC_COMM_START 0x000080100508ull -#define FF400_TX_PACKET_FORMAT 0x00008010050cull -#define FF400_ISOC_COMM_STOP 0x000080100510ull - -/* - * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, - * we can allocate between 0 and 7 channel. - */ -static int keep_resources(struct snd_ff *ff, unsigned int rate) -{ - enum snd_ff_stream_mode mode; - int i; - int err; - - // Check whether the given value is supported or not. - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - if (i >= CIP_SFC_COUNT) - return -EINVAL; - - err = snd_ff_stream_get_multiplier_mode(i, &mode); - if (err < 0) - return err; - - /* Keep resources for in-stream. */ - ff->tx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->tx_resources, - amdtp_stream_get_max_payload(&ff->tx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; - - /* Keep resources for out-stream. */ - err = amdtp_ff_set_parameters(&ff->rx_stream, rate, - ff->spec->pcm_playback_channels[mode]); - if (err < 0) - return err; - ff->rx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - fw_iso_resources_free(&ff->tx_resources); - - return err; -} - -static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) -{ - __le32 reg; - int err; - - err = keep_resources(ff, rate); - if (err < 0) - return err; - - /* Set the number of data blocks transferred in a second. */ - reg = cpu_to_le32(rate); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; - - msleep(100); - - /* - * Set isochronous channel and the number of quadlets of received - * packets. - */ - reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | - ff->rx_resources.channel); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0); - if (err < 0) - return err; - - /* - * Set isochronous channel and the number of quadlets of transmitted - * packet. - */ - /* TODO: investigate the purpose of this 0x80. */ - reg = cpu_to_le32((0x80 << 24) | - (ff->tx_resources.channel << 5) | - (ff->tx_stream.data_block_quadlets)); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0); - if (err < 0) - return err; - - /* Allow to transmit packets. */ - reg = cpu_to_le32(0x00000001); - return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_ISOC_COMM_START, ®, sizeof(reg), 0); -} - -static void ff400_finish_session(struct snd_ff *ff) -{ - __le32 reg; - - reg = cpu_to_le32(0x80000000); - snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); -} - -static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) -{ - int i; - - for (i = 0; i < length / 4; i++) { - u32 quad = le32_to_cpu(buf[i]); - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - - /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - } -} - -const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .handle_midi_msg = ff400_handle_midi_msg, - .begin_session = ff400_begin_session, - .finish_session = ff400_finish_session, -}; diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c deleted file mode 100644 index 2acbf6039770..000000000000 --- a/sound/firewire/fireface/ff-protocol-ff800.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * ff-protocol-ff800.c - a part of driver for RME Fireface series - * - * Copyright (c) 2018 Takashi Sakamoto - * - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include <linux/delay.h> - -#include "ff.h" - -#define FF800_STF 0x0000fc88f000 -#define FF800_RX_PACKET_FORMAT 0x0000fc88f004 -#define FF800_ALLOC_TX_STREAM 0x0000fc88f008 -#define FF800_ISOC_COMM_START 0x0000fc88f00c -#define FF800_TX_S800_FLAG 0x00000800 -#define FF800_ISOC_COMM_STOP 0x0000fc88f010 - -#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 - -static int allocate_rx_resources(struct snd_ff *ff) -{ - u32 data; - __le32 reg; - int err; - - // Controllers should allocate isochronous resources for rx stream. - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; - - // Set isochronous channel and the number of quadlets of rx packets. - data = ff->rx_stream.data_block_quadlets << 3; - data = (data << 8) | ff->rx_resources.channel; - reg = cpu_to_le32(data); - return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); -} - -static int allocate_tx_resources(struct snd_ff *ff) -{ - __le32 reg; - unsigned int count; - unsigned int tx_isoc_channel; - int err; - - reg = cpu_to_le32(ff->tx_stream.data_block_quadlets); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_ALLOC_TX_STREAM, ®, sizeof(reg), 0); - if (err < 0) - return err; - - // Wait till the format of tx packet is available. - count = 0; - while (count++ < 10) { - u32 data; - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF800_TX_PACKET_ISOC_CH, ®, sizeof(reg), 0); - if (err < 0) - return err; - - data = le32_to_cpu(reg); - if (data != 0xffffffff) { - tx_isoc_channel = data; - break; - } - - msleep(50); - } - if (count >= 10) - return -ETIMEDOUT; - - // NOTE: this is a makeshift to start OHCI 1394 IR context in the - // channel. On the other hand, 'struct fw_iso_resources.allocated' is - // not true and it's not deallocated at stop. - ff->tx_resources.channel = tx_isoc_channel; - - return 0; -} - -static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) -{ - __le32 reg; - int err; - - reg = cpu_to_le32(rate); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; - - // If starting isochronous communication immediately, change of STF has - // no effect. In this case, the communication runs based on former STF. - // Let's sleep for a bit. - msleep(100); - - err = allocate_rx_resources(ff); - if (err < 0) - return err; - - err = allocate_tx_resources(ff); - if (err < 0) - return err; - - reg = cpu_to_le32(0x80000000); - reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); - if (fw_parent_device(ff->unit)->max_speed == SCODE_800) - reg |= cpu_to_le32(FF800_TX_S800_FLAG); - return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_ISOC_COMM_START, ®, sizeof(reg), 0); -} - -static void ff800_finish_session(struct snd_ff *ff) -{ - __le32 reg; - - reg = cpu_to_le32(0x80000000); - snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_ISOC_COMM_STOP, ®, sizeof(reg), 0); -} - -static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) -{ - int i; - - for (i = 0; i < length / 4; i++) { - u8 byte = le32_to_cpu(buf[i]) & 0xff; - struct snd_rawmidi_substream *substream; - - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream) - snd_rawmidi_receive(substream, &byte, 1); - } -} - -const struct snd_ff_protocol snd_ff_protocol_ff800 = { - .handle_midi_msg = ff800_handle_midi_msg, - .begin_session = ff800_begin_session, - .finish_session = ff800_finish_session, -}; diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c new file mode 100644 index 000000000000..8d1c2c6e907b --- /dev/null +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +// ff-protocol-former.c - a part of driver for RME Fireface series +// +// Copyright (c) 2019 Takashi Sakamoto +// +// Licensed under the terms of the GNU General Public License, version 2. + +#include <linux/delay.h> + +#include "ff.h" + +#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull +/* For block write request. */ +#define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull +#define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull + +static int parse_clock_bits(u32 data, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + static const struct { + unsigned int rate; + u32 mask; + } *rate_entry, rate_entries[] = { + { 32000, 0x00000002, }, + { 44100, 0x00000000, }, + { 48000, 0x00000006, }, + { 64000, 0x0000000a, }, + { 88200, 0x00000008, }, + { 96000, 0x0000000e, }, + { 128000, 0x00000012, }, + { 176400, 0x00000010, }, + { 192000, 0x00000016, }, + }; + static const struct { + enum snd_ff_clock_src src; + u32 mask; + } *clk_entry, clk_entries[] = { + { SND_FF_CLOCK_SRC_ADAT1, 0x00000000, }, + { SND_FF_CLOCK_SRC_ADAT2, 0x00000400, }, + { SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, }, + { SND_FF_CLOCK_SRC_WORD, 0x00001000, }, + { SND_FF_CLOCK_SRC_LTC, 0x00001800, }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { + rate_entry = rate_entries + i; + if ((data & 0x0000001e) == rate_entry->mask) { + *rate = rate_entry->rate; + break; + } + } + if (i == ARRAY_SIZE(rate_entries)) + return -EIO; + + if (data & 0x00000001) { + *src = SND_FF_CLOCK_SRC_INTERNAL; + } else { + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + clk_entry = clk_entries + i; + if ((data & 0x00001c00) == clk_entry->mask) { + *src = clk_entry->src; + break; + } + } + if (i == ARRAY_SIZE(clk_entries)) + return -EIO; + } + + return 0; +} + +static int former_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return err; + data = le32_to_cpu(reg); + + return parse_clock_bits(data, rate, src); +} + +static int former_switch_fetching_mode(struct snd_ff *ff, bool enable) +{ + unsigned int count; + __le32 *reg; + int i; + int err; + + count = 0; + for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) + count = max(count, ff->spec->pcm_playback_channels[i]); + + reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + if (!enable) { + /* + * Each quadlet is corresponding to data channels in a data + * blocks in reverse order. Precisely, quadlets for available + * data channels should be enabled. Here, I take second best + * to fetch PCM frames from all of data channels regardless of + * stf. + */ + for (i = 0; i < count; ++i) + reg[i] = cpu_to_le32(0x00000001); + } + + err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, + FORMER_REG_FETCH_PCM_FRAMES, reg, + sizeof(__le32) * count, 0); + kfree(reg); + return err; +} + +static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) +{ + __le32 reg; + u32 data; + unsigned int rate; + enum snd_ff_clock_src src; + const char *label; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, + FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return; + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", + (data & 0x00000020) ? "Professional" : "Consumer", + (data & 0x00000040) ? "on" : "off"); + + snd_iprintf(buffer, "Optical output interface format: %s\n", + (data & 0x00000100) ? "S/PDIF" : "ADAT"); + + snd_iprintf(buffer, "Word output single speed: %s\n", + (data & 0x00002000) ? "on" : "off"); + + snd_iprintf(buffer, "S/PDIF input interface: %s\n", + (data & 0x00000200) ? "Optical" : "Coaxial"); + + err = parse_clock_bits(data, &rate, &src); + if (err < 0) + return; + label = snd_ff_proc_get_clk_label(src); + if (!label) + return; + + snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label); +} + +static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer) +{ + static const struct { + char *const label; + u32 locked_mask; + u32 synced_mask; + } *clk_entry, clk_entries[] = { + { "WDClk", 0x40000000, 0x20000000, }, + { "S/PDIF", 0x00080000, 0x00040000, }, + { "ADAT1", 0x00000400, 0x00001000, }, + { "ADAT2", 0x00000800, 0x00002000, }, + }; + static const struct { + char *const label; + u32 mask; + } *referred_entry, referred_entries[] = { + { "ADAT1", 0x00000000, }, + { "ADAT2", 0x00400000, }, + { "S/PDIF", 0x00c00000, }, + { "WDclk", 0x01000000, }, + { "TCO", 0x01400000, }, + }; + static const struct { + unsigned int rate; + u32 mask; + } *rate_entry, rate_entries[] = { + { 32000, 0x02000000, }, + { 44100, 0x04000000, }, + { 48000, 0x06000000, }, + { 64000, 0x08000000, }, + { 88200, 0x0a000000, }, + { 96000, 0x0c000000, }, + { 128000, 0x0e000000, }, + { 176400, 0x10000000, }, + { 192000, 0x12000000, }, + }; + __le32 reg[2]; + u32 data[2]; + int i; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, + FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0); + if (err < 0) + return; + data[0] = le32_to_cpu(reg[0]); + data[1] = le32_to_cpu(reg[1]); + + snd_iprintf(buffer, "External source detection:\n"); + + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + const char *state; + + clk_entry = clk_entries + i; + if (data[0] & clk_entry->locked_mask) { + if (data[0] & clk_entry->synced_mask) + state = "sync"; + else + state = "lock"; + } else { + state = "none"; + } + + snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state); + } + + snd_iprintf(buffer, "Referred clock:\n"); + + if (data[1] & 0x00000001) { + snd_iprintf(buffer, "Internal\n"); + } else { + unsigned int rate; + const char *label; + + for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) { + referred_entry = referred_entries + i; + if ((data[0] & 0x1e0000) == referred_entry->mask) { + label = referred_entry->label; + break; + } + } + if (i == ARRAY_SIZE(referred_entries)) + label = "none"; + + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { + rate_entry = rate_entries + i; + if ((data[0] & 0x1e000000) == rate_entry->mask) { + rate = rate_entry->rate; + break; + } + } + if (i == ARRAY_SIZE(rate_entries)) + rate = 0; + + snd_iprintf(buffer, "%s %d\n", label, rate); + } +} + +static void former_dump_status(struct snd_ff *ff, + struct snd_info_buffer *buffer) +{ + dump_clock_config(ff, buffer); + dump_sync_status(ff, buffer); +} + +static int former_fill_midi_msg(struct snd_ff *ff, + struct snd_rawmidi_substream *substream, + unsigned int port) +{ + u8 *buf = (u8 *)ff->msg_buf[port]; + int len; + int i; + + len = snd_rawmidi_transmit_peek(substream, buf, + SND_FF_MAXIMIM_MIDI_QUADS); + if (len <= 0) + return len; + + // One quadlet includes one byte. + for (i = len - 1; i >= 0; --i) + ff->msg_buf[port][i] = cpu_to_le32(buf[i]); + ff->rx_bytes[port] = len; + + return len; +} + +#define FF800_STF 0x0000fc88f000 +#define FF800_RX_PACKET_FORMAT 0x0000fc88f004 +#define FF800_ALLOC_TX_STREAM 0x0000fc88f008 +#define FF800_ISOC_COMM_START 0x0000fc88f00c +#define FF800_TX_S800_FLAG 0x00000800 +#define FF800_ISOC_COMM_STOP 0x0000fc88f010 + +#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 + +static int allocate_rx_resources(struct snd_ff *ff) +{ + u32 data; + __le32 reg; + int err; + + // Controllers should allocate isochronous resources for rx stream. + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + // Set isochronous channel and the number of quadlets of rx packets. + data = ff->rx_stream.data_block_quadlets << 3; + data = (data << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); +} + +static int allocate_tx_resources(struct snd_ff *ff) +{ + __le32 reg; + unsigned int count; + unsigned int tx_isoc_channel; + int err; + + reg = cpu_to_le32(ff->tx_stream.data_block_quadlets); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ALLOC_TX_STREAM, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Wait till the format of tx packet is available. + count = 0; + while (count++ < 10) { + u32 data; + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + FF800_TX_PACKET_ISOC_CH, ®, sizeof(reg), 0); + if (err < 0) + return err; + + data = le32_to_cpu(reg); + if (data != 0xffffffff) { + tx_isoc_channel = data; + break; + } + + msleep(50); + } + if (count >= 10) + return -ETIMEDOUT; + + // NOTE: this is a makeshift to start OHCI 1394 IR context in the + // channel. On the other hand, 'struct fw_iso_resources.allocated' is + // not true and it's not deallocated at stop. + ff->tx_resources.channel = tx_isoc_channel; + + return 0; +} + +static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +{ + __le32 reg; + int err; + + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // If starting isochronous communication immediately, change of STF has + // no effect. In this case, the communication runs based on former STF. + // Let's sleep for a bit. + msleep(100); + + err = allocate_rx_resources(ff); + if (err < 0) + return err; + + err = allocate_tx_resources(ff); + if (err < 0) + return err; + + reg = cpu_to_le32(0x80000000); + reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); + if (fw_parent_device(ff->unit)->max_speed == SCODE_800) + reg |= cpu_to_le32(FF800_TX_S800_FLAG); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ISOC_COMM_START, ®, sizeof(reg), 0); +} + +static void ff800_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x80000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ISOC_COMM_STOP, ®, sizeof(reg), 0); +} + +// Fireface 800 doesn't allow drivers to register lower 4 bytes of destination +// address. +// A write transaction to clear registered higher 4 bytes of destination address +// has an effect to suppress asynchronous transaction from device. +static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, + __le32 *buf, size_t length) +{ + int i; + + for (i = 0; i < length / 4; i++) { + u8 byte = le32_to_cpu(buf[i]) & 0xff; + struct snd_rawmidi_substream *substream; + + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream) + snd_rawmidi_receive(substream, &byte, 1); + } +} + +const struct snd_ff_protocol snd_ff_protocol_ff800 = { + .handle_midi_msg = ff800_handle_midi_msg, + .fill_midi_msg = former_fill_midi_msg, + .get_clock = former_get_clock, + .switch_fetching_mode = former_switch_fetching_mode, + .begin_session = ff800_begin_session, + .finish_session = ff800_finish_session, + .dump_status = former_dump_status, +}; + +#define FF400_STF 0x000080100500ull +#define FF400_RX_PACKET_FORMAT 0x000080100504ull +#define FF400_ISOC_COMM_START 0x000080100508ull +#define FF400_TX_PACKET_FORMAT 0x00008010050cull +#define FF400_ISOC_COMM_STOP 0x000080100510ull + +/* + * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, + * we can allocate between 0 and 7 channel. + */ +static int keep_resources(struct snd_ff *ff, unsigned int rate) +{ + enum snd_ff_stream_mode mode; + int i; + int err; + + // Check whether the given value is supported or not. + for (i = 0; i < CIP_SFC_COUNT; i++) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + return err; + + /* Keep resources for in-stream. */ + ff->tx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->tx_resources, + amdtp_stream_get_max_payload(&ff->tx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + /* Keep resources for out-stream. */ + ff->rx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + fw_iso_resources_free(&ff->tx_resources); + + return err; +} + +static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) +{ + __le32 reg; + int err; + + err = keep_resources(ff, rate); + if (err < 0) + return err; + + /* Set the number of data blocks transferred in a second. */ + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + msleep(100); + + /* + * Set isochronous channel and the number of quadlets of received + * packets. + */ + reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | + ff->rx_resources.channel); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* + * Set isochronous channel and the number of quadlets of transmitted + * packet. + */ + /* TODO: investigate the purpose of this 0x80. */ + reg = cpu_to_le32((0x80 << 24) | + (ff->tx_resources.channel << 5) | + (ff->tx_stream.data_block_quadlets)); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* Allow to transmit packets. */ + reg = cpu_to_le32(0x00000001); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_ISOC_COMM_START, ®, sizeof(reg), 0); +} + +static void ff400_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x80000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); +} + +// For Fireface 400, lower 4 bytes of destination address is configured by bit +// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can +// select one of 4 options: +// +// bit flags: offset of destination address +// - 0x04000000: 0x'....'....'0000'0000 +// - 0x08000000: 0x'....'....'0000'0080 +// - 0x10000000: 0x'....'....'0000'0100 +// - 0x20000000: 0x'....'....'0000'0180 +// +// Drivers can suppress the device to transfer asynchronous transactions by +// using below 2 bits. +// - 0x01000000: suppress transmission +// - 0x02000000: suppress transmission +// +// Actually, the register is write-only and includes the other options such as +// input attenuation. This driver allocates destination address with '0000'0000 +// in its lower offset and expects userspace application to configure the +// register for it. +static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset, + __le32 *buf, size_t length) +{ + int i; + + for (i = 0; i < length / 4; i++) { + u32 quad = le32_to_cpu(buf[i]); + u8 byte; + unsigned int index; + struct snd_rawmidi_substream *substream; + + /* Message in first port. */ + /* + * This value may represent the index of this unit when the same + * units are on the same IEEE 1394 bus. This driver doesn't use + * it. + */ + index = (quad >> 8) & 0xff; + if (index > 0) { + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream != NULL) { + byte = quad & 0xff; + snd_rawmidi_receive(substream, &byte, 1); + } + } + + /* Message in second port. */ + index = (quad >> 24) & 0xff; + if (index > 0) { + substream = READ_ONCE(ff->tx_midi_substreams[1]); + if (substream != NULL) { + byte = (quad >> 16) & 0xff; + snd_rawmidi_receive(substream, &byte, 1); + } + } + } +} + +const struct snd_ff_protocol snd_ff_protocol_ff400 = { + .handle_midi_msg = ff400_handle_midi_msg, + .fill_midi_msg = former_fill_midi_msg, + .get_clock = former_get_clock, + .switch_fetching_mode = former_switch_fetching_mode, + .begin_session = ff400_begin_session, + .finish_session = ff400_finish_session, + .dump_status = former_dump_status, +}; diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c new file mode 100644 index 000000000000..c8236ff89b7f --- /dev/null +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0 +// ff-protocol-latter - a part of driver for RME Fireface series +// +// Copyright (c) 2019 Takashi Sakamoto +// +// Licensed under the terms of the GNU General Public License, version 2. + +#include <linux/delay.h> + +#include "ff.h" + +#define LATTER_STF 0xffff00000004 +#define LATTER_ISOC_CHANNELS 0xffff00000008 +#define LATTER_ISOC_START 0xffff0000000c +#define LATTER_FETCH_MODE 0xffff00000010 +#define LATTER_SYNC_STATUS 0x0000801c0000 + +static int parse_clock_bits(u32 data, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + static const struct { + unsigned int rate; + u32 flag; + } *rate_entry, rate_entries[] = { + { 32000, 0x00000000, }, + { 44100, 0x01000000, }, + { 48000, 0x02000000, }, + { 64000, 0x04000000, }, + { 88200, 0x05000000, }, + { 96000, 0x06000000, }, + { 128000, 0x08000000, }, + { 176400, 0x09000000, }, + { 192000, 0x0a000000, }, + }; + static const struct { + enum snd_ff_clock_src src; + u32 flag; + } *clk_entry, clk_entries[] = { + { SND_FF_CLOCK_SRC_SPDIF, 0x00000200, }, + { SND_FF_CLOCK_SRC_ADAT1, 0x00000400, }, + { SND_FF_CLOCK_SRC_WORD, 0x00000600, }, + { SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { + rate_entry = rate_entries + i; + if ((data & 0x0f000000) == rate_entry->flag) { + *rate = rate_entry->rate; + break; + } + } + if (i == ARRAY_SIZE(rate_entries)) + return -EIO; + + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + clk_entry = clk_entries + i; + if ((data & 0x000e00) == clk_entry->flag) { + *src = clk_entry->src; + break; + } + } + if (i == ARRAY_SIZE(clk_entries)) + return -EIO; + + return 0; +} + +static int latter_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + LATTER_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return err; + data = le32_to_cpu(reg); + + return parse_clock_bits(data, rate, src); +} + +static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable) +{ + u32 data; + __le32 reg; + + if (enable) + data = 0x00000000; + else + data = 0xffffffff; + reg = cpu_to_le32(data); + + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_FETCH_MODE, ®, sizeof(reg), 0); +} + +static int keep_resources(struct snd_ff *ff, unsigned int rate) +{ + enum snd_ff_stream_mode mode; + int i; + int err; + + // Check whether the given value is supported or not. + for (i = 0; i < CIP_SFC_COUNT; i++) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + return err; + + /* Keep resources for in-stream. */ + ff->tx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->tx_resources, + amdtp_stream_get_max_payload(&ff->tx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + /* Keep resources for out-stream. */ + ff->rx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + fw_iso_resources_free(&ff->tx_resources); + + return err; +} + +static int latter_begin_session(struct snd_ff *ff, unsigned int rate) +{ + static const struct { + unsigned int stf; + unsigned int code; + unsigned int flag; + } *entry, rate_table[] = { + { 32000, 0x00, 0x92, }, + { 44100, 0x02, 0x92, }, + { 48000, 0x04, 0x92, }, + { 64000, 0x08, 0x8e, }, + { 88200, 0x0a, 0x8e, }, + { 96000, 0x0c, 0x8e, }, + { 128000, 0x10, 0x8c, }, + { 176400, 0x12, 0x8c, }, + { 192000, 0x14, 0x8c, }, + }; + u32 data; + __le32 reg; + unsigned int count; + int i; + int err; + + for (i = 0; i < ARRAY_SIZE(rate_table); ++i) { + entry = rate_table + i; + if (entry->stf == rate) + break; + } + if (i == ARRAY_SIZE(rate_table)) + return -EINVAL; + + reg = cpu_to_le32(entry->code); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Confirm to shift transmission clock. + count = 0; + while (count++ < 10) { + unsigned int curr_rate; + enum snd_ff_clock_src src; + + err = latter_get_clock(ff, &curr_rate, &src); + if (err < 0) + return err; + + if (curr_rate == rate) + break; + } + if (count == 10) + return -ETIMEDOUT; + + err = keep_resources(ff, rate); + if (err < 0) + return err; + + data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_ISOC_CHANNELS, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Always use the maximum number of data channels in data block of + // packet. + reg = cpu_to_le32(entry->flag); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_ISOC_START, ®, sizeof(reg), 0); +} + +static void latter_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x00000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_ISOC_START, ®, sizeof(reg), 0); +} + +static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer) +{ + static const struct { + char *const label; + u32 locked_mask; + u32 synced_mask; + } *clk_entry, clk_entries[] = { + { "S/PDIF", 0x00000001, 0x00000010, }, + { "ADAT", 0x00000002, 0x00000020, }, + { "WDClk", 0x00000004, 0x00000040, }, + }; + __le32 reg; + u32 data; + unsigned int rate; + enum snd_ff_clock_src src; + const char *label; + int i; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + LATTER_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return; + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "External source detection:\n"); + + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + clk_entry = clk_entries + i; + snd_iprintf(buffer, "%s: ", clk_entry->label); + if (data & clk_entry->locked_mask) { + if (data & clk_entry->synced_mask) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + } + + err = parse_clock_bits(data, &rate, &src); + if (err < 0) + return; + label = snd_ff_proc_get_clk_label(src); + if (!label) + return; + + snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate); +} + +// NOTE: transactions are transferred within 0x00-0x7f in allocated range of +// address. This seems to be for check of discontinuity in receiver side. +// +// Like Fireface 400, drivers can select one of 4 options for lower 4 bytes of +// destination address by bit flags in quadlet register (little endian) at +// 0x'ffff'0000'0014: +// +// bit flags: offset of destination address +// - 0x00002000: 0x'....'....'0000'0000 +// - 0x00004000: 0x'....'....'0000'0080 +// - 0x00008000: 0x'....'....'0000'0100 +// - 0x00010000: 0x'....'....'0000'0180 +// +// Drivers can suppress the device to transfer asynchronous transactions by +// clear these bit flags. +// +// Actually, the register is write-only and includes the other settings such as +// input attenuation. This driver allocates for the first option +// (0x'....'....'0000'0000) and expects userspace application to configure the +// register for it. +static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, + __le32 *buf, size_t length) +{ + u32 data = le32_to_cpu(*buf); + unsigned int index = (data & 0x000000f0) >> 4; + u8 byte[3]; + struct snd_rawmidi_substream *substream; + unsigned int len; + + if (index >= ff->spec->midi_in_ports) + return; + + switch (data & 0x0000000f) { + case 0x00000008: + case 0x00000009: + case 0x0000000a: + case 0x0000000b: + case 0x0000000e: + len = 3; + break; + case 0x0000000c: + case 0x0000000d: + len = 2; + break; + default: + len = data & 0x00000003; + if (len == 0) + len = 3; + break; + } + + byte[0] = (data & 0x0000ff00) >> 8; + byte[1] = (data & 0x00ff0000) >> 16; + byte[2] = (data & 0xff000000) >> 24; + + substream = READ_ONCE(ff->tx_midi_substreams[index]); + if (substream) + snd_rawmidi_receive(substream, byte, len); +} + +/* + * When return minus value, given argument is not MIDI status. + * When return 0, given argument is a beginning of system exclusive. + * When return the others, given argument is MIDI data. + */ +static inline int calculate_message_bytes(u8 status) +{ + switch (status) { + case 0xf6: /* Tune request. */ + case 0xf8: /* Timing clock. */ + case 0xfa: /* Start. */ + case 0xfb: /* Continue. */ + case 0xfc: /* Stop. */ + case 0xfe: /* Active sensing. */ + case 0xff: /* System reset. */ + return 1; + case 0xf1: /* MIDI time code quarter frame. */ + case 0xf3: /* Song select. */ + return 2; + case 0xf2: /* Song position pointer. */ + return 3; + case 0xf0: /* Exclusive. */ + return 0; + case 0xf7: /* End of exclusive. */ + break; + case 0xf4: /* Undefined. */ + case 0xf5: /* Undefined. */ + case 0xf9: /* Undefined. */ + case 0xfd: /* Undefined. */ + break; + default: + switch (status & 0xf0) { + case 0x80: /* Note on. */ + case 0x90: /* Note off. */ + case 0xa0: /* Polyphonic key pressure. */ + case 0xb0: /* Control change and Mode change. */ + case 0xe0: /* Pitch bend change. */ + return 3; + case 0xc0: /* Program change. */ + case 0xd0: /* Channel pressure. */ + return 2; + default: + break; + } + break; + } + + return -EINVAL; +} + +static int latter_fill_midi_msg(struct snd_ff *ff, + struct snd_rawmidi_substream *substream, + unsigned int port) +{ + u32 data = {0}; + u8 *buf = (u8 *)&data; + int consumed; + + buf[0] = port << 4; + consumed = snd_rawmidi_transmit_peek(substream, buf + 1, 3); + if (consumed <= 0) + return consumed; + + if (!ff->on_sysex[port]) { + if (buf[1] != 0xf0) { + if (consumed < calculate_message_bytes(buf[1])) + return 0; + } else { + // The beginning of exclusives. + ff->on_sysex[port] = true; + } + + buf[0] |= consumed; + } else { + if (buf[1] != 0xf7) { + if (buf[2] == 0xf7 || buf[3] == 0xf7) { + // Transfer end code at next time. + consumed -= 1; + } + + buf[0] |= consumed; + } else { + // The end of exclusives. + ff->on_sysex[port] = false; + consumed = 1; + buf[0] |= 0x0f; + } + } + + ff->msg_buf[port][0] = cpu_to_le32(data); + ff->rx_bytes[port] = consumed; + + return 1; +} + +const struct snd_ff_protocol snd_ff_protocol_latter = { + .handle_midi_msg = latter_handle_midi_msg, + .fill_midi_msg = latter_fill_midi_msg, + .get_clock = latter_get_clock, + .switch_fetching_mode = latter_switch_fetching_mode, + .begin_session = latter_begin_session, + .finish_session = latter_finish_session, + .dump_status = latter_dump_status, +}; diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index a490e4553721..a8a90f1ae09e 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -37,44 +37,10 @@ static void release_resources(struct snd_ff *ff) fw_iso_resources_free(&ff->rx_resources); } -static int switch_fetching_mode(struct snd_ff *ff, bool enable) -{ - unsigned int count; - __le32 *reg; - int i; - int err; - - count = 0; - for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) - count = max(count, ff->spec->pcm_playback_channels[i]); - - reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); - if (!reg) - return -ENOMEM; - - if (!enable) { - /* - * Each quadlet is corresponding to data channels in a data - * blocks in reverse order. Precisely, quadlets for available - * data channels should be enabled. Here, I take second best - * to fetch PCM frames from all of data channels regardless of - * stf. - */ - for (i = 0; i < count; ++i) - reg[i] = cpu_to_le32(0x00000001); - } - - err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, - SND_FF_REG_FETCH_PCM_FRAMES, reg, - sizeof(__le32) * count, 0); - kfree(reg); - return err; -} - static inline void finish_session(struct snd_ff *ff) { ff->spec->protocol->finish_session(ff); - switch_fetching_mode(ff, false); + ff->spec->protocol->switch_fetching_mode(ff, false); } static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) @@ -147,7 +113,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (ff->substreams_counter == 0) return 0; - err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); + err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); if (err < 0) return err; if (curr_rate != rate || @@ -206,7 +172,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) goto error; } - err = switch_fetching_mode(ff, true); + err = ff->spec->protocol->switch_fetching_mode(ff, true); if (err < 0) goto error; } diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 5f4ddfd55403..0d6ad19363b8 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -8,72 +8,6 @@ #include "ff.h" -#define SND_FF_REG_MIDI_RX_PORT_0 0x000080180000ull -#define SND_FF_REG_MIDI_RX_PORT_1 0x000080190000ull - -int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src) -{ - __le32 reg; - u32 data; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); - if (err < 0) - return err; - data = le32_to_cpu(reg); - - /* Calculate sampling rate. */ - switch ((data >> 1) & 0x03) { - case 0x01: - *rate = 32000; - break; - case 0x00: - *rate = 44100; - break; - case 0x03: - *rate = 48000; - break; - case 0x02: - default: - return -EIO; - } - - if (data & 0x08) - *rate *= 2; - else if (data & 0x10) - *rate *= 4; - - /* Calculate source of clock. */ - if (data & 0x01) { - *src = SND_FF_CLOCK_SRC_INTERNAL; - } else { - /* TODO: 0x02, 0x06, 0x07? */ - switch ((data >> 10) & 0x07) { - case 0x00: - *src = SND_FF_CLOCK_SRC_ADAT1; - break; - case 0x01: - *src = SND_FF_CLOCK_SRC_ADAT2; - break; - case 0x03: - *src = SND_FF_CLOCK_SRC_SPDIF; - break; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - default: - return -EIO; - } - } - - return 0; -} - static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port, int rcode) { @@ -117,23 +51,17 @@ static void finish_transmit_midi1_msg(struct fw_card *card, int rcode, finish_transmit_midi_msg(ff, 1, rcode); } -static inline void fill_midi_buf(struct snd_ff *ff, unsigned int port, - unsigned int index, u8 byte) -{ - ff->msg_buf[port][index] = cpu_to_le32(byte); -} - static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) { struct snd_rawmidi_substream *substream = READ_ONCE(ff->rx_midi_substreams[port]); - u8 *buf = (u8 *)ff->msg_buf[port]; - int i, len; + int quad_count; struct fw_device *fw_dev = fw_parent_device(ff->unit); unsigned long long addr; int generation; fw_transaction_callback_t callback; + int tcode; if (substream == NULL || snd_rawmidi_transmit_empty(substream)) return; @@ -147,26 +75,26 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) return; } - len = snd_rawmidi_transmit_peek(substream, buf, - SND_FF_MAXIMIM_MIDI_QUADS); - if (len <= 0) + quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port); + if (quad_count <= 0) return; - for (i = len - 1; i >= 0; i--) - fill_midi_buf(ff, port, i, buf[i]); - if (port == 0) { - addr = SND_FF_REG_MIDI_RX_PORT_0; + addr = ff->spec->midi_rx_addrs[0]; callback = finish_transmit_midi0_msg; } else { - addr = SND_FF_REG_MIDI_RX_PORT_1; + addr = ff->spec->midi_rx_addrs[1]; callback = finish_transmit_midi1_msg; } /* Set interval to next transaction. */ ff->next_ktime[port] = ktime_add_ns(ktime_get(), - len * 8 * NSEC_PER_SEC / 31250); - ff->rx_bytes[port] = len; + ff->rx_bytes[port] * 8 * NSEC_PER_SEC / 31250); + + if (quad_count == 1) + tcode = TCODE_WRITE_QUADLET_REQUEST; + else + tcode = TCODE_WRITE_BLOCK_REQUEST; /* * In Linux FireWire core, when generation is updated with memory @@ -178,10 +106,9 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) */ generation = fw_dev->generation; smp_rmb(); - fw_send_request(fw_dev->card, &ff->transactions[port], - TCODE_WRITE_BLOCK_REQUEST, + fw_send_request(fw_dev->card, &ff->transactions[port], tcode, fw_dev->node_id, generation, fw_dev->max_speed, - addr, &ff->msg_buf[port], len * 4, + addr, &ff->msg_buf[port], quad_count * 4, callback, &ff->transactions[port]); } @@ -209,7 +136,9 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_COMPLETE); - ff->spec->protocol->handle_midi_msg(ff, buf, length); + offset -= ff->async_handler.offset; + ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf, + length); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -217,7 +146,7 @@ static int allocate_own_address(struct snd_ff *ff, int i) struct fw_address_region midi_msg_region; int err; - ff->async_handler.length = SND_FF_MAXIMIM_MIDI_QUADS * 4; + ff->async_handler.length = ff->spec->midi_addr_range; ff->async_handler.address_callback = handle_midi_msg; ff->async_handler.callback_data = ff; @@ -236,35 +165,13 @@ static int allocate_own_address(struct snd_ff *ff, int i) return err; } -/* - * Controllers are allowed to register higher 4 bytes of address to receive - * the transactions. Different models have different registers for this purpose; - * e.g. 0x'0000'8010'03f4 for Fireface 400. - * The controllers are not allowed to register lower 4 bytes of the address. - * They are forced to select one of 4 options for the part of address by writing - * corresponding bits to 0x'0000'8010'051f. - * - * The 3rd-6th bits of this register are flags to indicate lower 4 bytes of - * address to which the device transferrs the transactions. In short: - * - 0x20: 0x'....'....'0000'0180 - * - 0x10: 0x'....'....'0000'0100 - * - 0x08: 0x'....'....'0000'0080 - * - 0x04: 0x'....'....'0000'0000 - * - * This driver configure 0x'....'....'0000'0000 to receive MIDI messages from - * units. The 3rd bit of the register should be configured, however this driver - * deligates this task to userspace applications due to a restriction that this - * register is write-only and the other bits have own effects. - * - * Unlike Fireface 800, Fireface 400 cancels transferring asynchronous - * transactions when the 1st and 2nd of the register stand. These two bits have - * the same effect. - * - 0x02, 0x01: cancel transferring - * - * On the other hand, the bits have no effect on Fireface 800. This model - * cancels asynchronous transactions when the higher 4 bytes of address is - * overwritten with zero. - */ +// Controllers are allowed to register higher 4 bytes of destination address to +// receive asynchronous transactions for MIDI messages, while the way to +// register lower 4 bytes of address is different depending on protocols. For +// details, please refer to comments in protocol implementations. +// +// This driver expects userspace applications to configure registers for the +// lower address because in most cases such registers has the other settings. int snd_ff_transaction_reregister(struct snd_ff *ff) { struct fw_card *fw_card = fw_parent_device(ff->unit)->card; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 36575f4159d1..a9611157f4c8 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -153,6 +153,8 @@ static const struct snd_ff_spec spec_ff800 = { .midi_out_ports = 1, .protocol = &snd_ff_protocol_ff800, .midi_high_addr = 0x000200000320ull, + .midi_addr_range = 12, + .midi_rx_addrs = {0x000080180000ull, 0}, }; static const struct snd_ff_spec spec_ff400 = { @@ -163,6 +165,20 @@ static const struct snd_ff_spec spec_ff400 = { .midi_out_ports = 2, .protocol = &snd_ff_protocol_ff400, .midi_high_addr = 0x0000801003f4ull, + .midi_addr_range = SND_FF_MAXIMIM_MIDI_QUADS * 4, + .midi_rx_addrs = {0x000080180000ull, 0x000080190000ull}, +}; + +static const struct snd_ff_spec spec_ucx = { + .name = "FirefaceUCX", + .pcm_capture_channels = {18, 14, 12}, + .pcm_playback_channels = {18, 14, 12}, + .midi_in_ports = 2, + .midi_out_ports = 2, + .protocol = &snd_ff_protocol_latter, + .midi_high_addr = 0xffff00000034ull, + .midi_addr_range = 0x80, + .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, }; static const struct ieee1394_device_id snd_ff_id_table[] = { @@ -190,6 +206,18 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, }, + // Fireface UCX. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = 0x000004, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ucx, + }, {} }; MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 7dfc7745a914..ed8fea0ff5e1 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -35,11 +35,6 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 -#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull -/* For block write request. */ -#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull -#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull - enum snd_ff_stream_mode { SND_FF_STREAM_MODE_LOW = 0, SND_FF_STREAM_MODE_MID, @@ -59,6 +54,8 @@ struct snd_ff_spec { const struct snd_ff_protocol *protocol; u64 midi_high_addr; + u8 midi_addr_range; + u64 midi_rx_addrs[SND_FF_OUT_MIDI_PORTS]; }; struct snd_ff { @@ -78,7 +75,7 @@ struct snd_ff { /* TO handle MIDI rx. */ struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS]; - u8 running_status[SND_FF_OUT_MIDI_PORTS]; + bool on_sysex[SND_FF_OUT_MIDI_PORTS]; __le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS]; struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS]; struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS]; @@ -108,16 +105,23 @@ enum snd_ff_clock_src { }; struct snd_ff_protocol { - void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); + void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset, + __le32 *buf, size_t length); + int (*fill_midi_msg)(struct snd_ff *ff, + struct snd_rawmidi_substream *substream, + unsigned int port); + int (*get_clock)(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src); + int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); + void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer); }; extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff400; +extern const struct snd_ff_protocol snd_ff_protocol_latter; -int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src); int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff); void snd_ff_transaction_unregister(struct snd_ff *ff); @@ -142,6 +146,7 @@ int snd_ff_stream_lock_try(struct snd_ff *ff); void snd_ff_stream_lock_release(struct snd_ff *ff); void snd_ff_proc_init(struct snd_ff *ff); +const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src); int snd_ff_create_midi_devices(struct snd_ff *ff); diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c index 779ecec5af62..9fa5c34a9572 100644 --- a/sound/firewire/fireworks/fireworks_proc.c +++ b/sound/firewire/fireworks/fireworks_proc.c @@ -199,12 +199,8 @@ add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name, struct snd_info_entry *entry; entry = snd_info_create_card_entry(efw->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, efw, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, efw, op); } void snd_efw_proc_init(struct snd_efw *efw) @@ -220,10 +216,6 @@ void snd_efw_proc_init(struct snd_efw *efw) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(efw, root, "clock", proc_read_clock); add_node(efw, root, "firmware", proc_read_hwinfo); diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index f0555a24d90e..6c9b743ea74b 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -136,7 +136,9 @@ static void read_pcm_s32(struct amdtp_stream *s, byte = (u8 *)buffer + p->pcm_byte_offset; for (c = 0; c < channels; ++c) { - *dst = (byte[0] << 24) | (byte[1] << 16) | byte[2]; + *dst = (byte[0] << 24) | + (byte[1] << 16) | + (byte[2] << 8); byte += 3; dst++; } diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c index ab6830a6d242..94327853620a 100644 --- a/sound/firewire/motu/motu-proc.c +++ b/sound/firewire/motu/motu-proc.c @@ -87,12 +87,8 @@ static void add_node(struct snd_motu *motu, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(motu->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, motu, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, motu, op); } void snd_motu_proc_init(struct snd_motu *motu) @@ -108,10 +104,6 @@ void snd_motu_proc_init(struct snd_motu *motu) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(motu, root, "clock", proc_read_clock); add_node(motu, root, "format", proc_read_format); diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c index 27dac071bc73..644107e3782e 100644 --- a/sound/firewire/oxfw/oxfw-proc.c +++ b/sound/firewire/oxfw/oxfw-proc.c @@ -83,12 +83,8 @@ static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(oxfw->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, oxfw, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, oxfw, op); } void snd_oxfw_proc_init(struct snd_oxfw *oxfw) @@ -104,10 +100,6 @@ void snd_oxfw_proc_init(struct snd_oxfw *oxfw) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(oxfw, root, "formation", proc_read_formation); } diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c index fee3bf32a0da..8bc8d277394a 100644 --- a/sound/firewire/tascam/tascam-proc.c +++ b/sound/firewire/tascam/tascam-proc.c @@ -58,12 +58,8 @@ static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(tscm->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, tscm, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, tscm, op); } void snd_tscm_proc_init(struct snd_tscm *tscm) @@ -79,10 +75,6 @@ void snd_tscm_proc_init(struct snd_tscm *tscm) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(tscm, root, "firmware", proc_read_firmware); } |