[alsa-devel] [PATCH 0/5] ALSA: fireface: add support for Fireface 800 with streaming functionality
Hi,
My former patchset adds support for Fireface 800 with MIDI functionality only[1]. This patchset updates driver with packet streaming functionality. As a result, both of PCM frames/MIDI messages are available.
This patchset is based on my former patch to fix a bug to switch frame fetching mode:
[alsa-devel] [PATCH] ALSA: fireface: fix for state to fetch PCM frames http://mailman.alsa-project.org/pipermail/alsa-devel/2018-December/143150.ht...
Unfortunately, my initial commit of ALSA fireface driver in v4.12 kernel includes bugs to handle tx isochronous packets. When testing this patchset, please apply below patchset in advance:
[alsa-devel] [PATCH 0/3] ALSA: firewire-lib: fixes for processing packets without CIP header http://mailman.alsa-project.org/pipermail/alsa-devel/2018-December/143146.ht...
For testers, a remote branch including overall patches is available in my remote repository in github.com[2]. They can be backport to Linux kernel v4.17 or later.
I note that this driver can generate hissing noise long-periodically as I reported for Fireface 400[3]. The cause is not cleared yet.
[1] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-December/142826.ht... [2] https://github.com/takaswie/snd-firewire-improve/tree/topic/ff800-pcm [3] http://mailman.alsa-project.org/pipermail/alsa-devel/2015-December/102261.ht...
Takashi Sakamoto (5): ALSA: fireface: share helper function to switch fetching mode ALSA: fireface: code refactoring to handle multiplier mode ALSA: fireface: allocate isochronous resources in mode-specific implementation ALSA: fireface: add support for packet streaming on Fireface 800 ALSA: fireface: code refactoring to handle model-specific registers
sound/firewire/fireface/ff-pcm.c | 33 ++++-- sound/firewire/fireface/ff-protocol-ff400.c | 81 +++++++------ sound/firewire/fireface/ff-protocol-ff800.c | 116 ++++++++++++++++++ sound/firewire/fireface/ff-stream.c | 124 +++++++++++--------- sound/firewire/fireface/ff-transaction.c | 4 +- sound/firewire/fireface/ff.c | 37 +++--- sound/firewire/fireface/ff.h | 21 ++-- 7 files changed, 283 insertions(+), 133 deletions(-)
Both of Fireface 400/800 have the same register to switch frame fetching mode regardless of difference of available number of PCM frames in rx isochronous packet.
This commit moves a helper function from model-dependent implementation.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff400.c | 30 ---------------- sound/firewire/fireface/ff-stream.c | 38 +++++++++++++++++++-- sound/firewire/fireface/ff.h | 1 - 3 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index fd257051d4a4..374009545936 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -76,35 +76,6 @@ static void ff400_finish_session(struct snd_ff *ff) FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); }
-static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) -{ - __le32 *reg; - int i; - int err; - - reg = kcalloc(18, sizeof(__le32), GFP_KERNEL); - if (reg == NULL) - 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 < 18; ++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) * 18, 0); - kfree(reg); - return err; -} - static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) { int i; @@ -146,5 +117,4 @@ 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, - .switch_fetching_mode = ff400_switch_fetching_mode, }; diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 59ca2e84d41c..e6fa6362dab7 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -73,10 +73,44 @@ 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_MODES; ++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); - ff->spec->protocol->switch_fetching_mode(ff, false); + switch_fetching_mode(ff, false); }
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) @@ -188,7 +222,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) goto error; }
- err = ff->spec->protocol->switch_fetching_mode(ff, true); + err = switch_fetching_mode(ff, true); if (err < 0) goto error; } diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 6e4a8197d3ca..17332d9ae3f2 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -111,7 +111,6 @@ struct snd_ff_protocol { void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); - int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); };
extern const struct snd_ff_protocol snd_ff_protocol_ff800;
Fireface 400/800 use three modes against the number of data channels in data block for both tx/rx packets.
This commit adds refactoring for it. Some enumerators are added to represent each of mode and a function is added to calculate the mode from sampling frequency code (sfc).
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-pcm.c | 33 ++++++++++++++++--------- sound/firewire/fireface/ff-stream.c | 38 +++++++++++++++++++---------- sound/firewire/fireface/ff.h | 15 +++++++++--- 3 files changed, 58 insertions(+), 28 deletions(-)
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 63b0be6f05e8..d0bc96b20a65 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -8,11 +8,6 @@
#include "ff.h"
-static inline unsigned int get_multiplier_mode_with_index(unsigned int index) -{ - return ((int)index - 1) / 2; -} - static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -24,10 +19,16 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; + if (!snd_interval_test(c, pcm_channels[mode])) continue;
@@ -49,10 +50,16 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; + if (!snd_interval_test(r, amdtp_rate_table[i])) continue;
@@ -66,7 +73,6 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { - unsigned int mode; unsigned int rate, channels; int i;
@@ -76,7 +82,12 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw, hw->rate_max = 0;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue;
channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index e6fa6362dab7..e6a8229a9d82 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -10,19 +10,23 @@
#define CALLBACK_TIMEOUT_MS 200
-static int get_rate_mode(unsigned int rate, unsigned int *mode) +int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, + enum snd_ff_stream_mode *mode) { - int i; - - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - - if (i == CIP_SFC_COUNT) + static const enum snd_ff_stream_mode modes[] = { + [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID, + [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID, + [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH, + [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH, + }; + + if (sfc >= CIP_SFC_COUNT) return -EINVAL;
- *mode = ((int)i - 1) / 2; + *mode = modes[sfc];
return 0; } @@ -33,10 +37,18 @@ static int get_rate_mode(unsigned int rate, unsigned int *mode) */ static int keep_resources(struct snd_ff *ff, unsigned int rate) { - int mode; + enum snd_ff_stream_mode mode; + int i; int err;
- err = get_rate_mode(rate, &mode); + 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;
@@ -81,7 +93,7 @@ static int switch_fetching_mode(struct snd_ff *ff, bool enable) int err;
count = 0; - for (i = 0; i < SND_FF_STREAM_MODES; ++i) + 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); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 17332d9ae3f2..9fdda4fbdbba 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -31,8 +31,6 @@ #include "../amdtp-stream.h" #include "../iso-resources.h"
-#define SND_FF_STREAM_MODES 3 - #define SND_FF_MAXIMIM_MIDI_QUADS 9 #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 @@ -42,6 +40,13 @@ #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, + SND_FF_STREAM_MODE_HIGH, + SND_FF_STREAM_MODE_COUNT, +}; + enum snd_ff_reg_type { SND_FF_REG_TYPE_MIDI_HIGH_ADDR = 0, SND_FF_REG_TYPE_COUNT, @@ -51,8 +56,8 @@ struct snd_ff_protocol; struct snd_ff_spec { const char *const name;
- const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES]; - const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES]; + const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT]; + const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT];
unsigned int midi_in_ports; unsigned int midi_out_ports; @@ -129,6 +134,8 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir);
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, + enum snd_ff_stream_mode *mode); int snd_ff_stream_init_duplex(struct snd_ff *ff); void snd_ff_stream_destroy_duplex(struct snd_ff *ff); int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
The way to maintain isochronous resources on bus is different between Fireface 400/800.
This commit is a preparation. This commit moves a function to allocate resource to model-dependent implementation.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff400.c | 51 +++++++++++++-- sound/firewire/fireface/ff-stream.c | 72 +++++++-------------- 2 files changed, 68 insertions(+), 55 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 374009545936..2280fab9b3c7 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -15,19 +15,60 @@ #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull
-static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) +/* + * 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) { - __le32 reg; - int i, err; + enum snd_ff_stream_mode mode; + int i; + int err;
- /* Check whether the given value is supported or not. */ + // 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) + 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, diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index e6a8229a9d82..a490e4553721 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -31,54 +31,6 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, return 0; }
-/* - * 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; - - 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. */ - err = amdtp_ff_set_parameters(&ff->tx_stream, rate, - ff->spec->pcm_capture_channels[mode]); - if (err < 0) - return err; - 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 void release_resources(struct snd_ff *ff) { fw_iso_resources_free(&ff->tx_resources); @@ -214,9 +166,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) * packets. Then, the device transfers packets. */ if (!amdtp_stream_running(&ff->rx_stream)) { - err = keep_resources(ff, rate); + enum snd_ff_stream_mode mode; + int i; + + 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) - goto error; + return err; + + err = amdtp_ff_set_parameters(&ff->tx_stream, rate, + ff->spec->pcm_capture_channels[mode]); + if (err < 0) + return err; + + err = amdtp_ff_set_parameters(&ff->rx_stream, rate, + ff->spec->pcm_playback_channels[mode]); + if (err < 0) + return err;
err = ff->spec->protocol->begin_session(ff, rate); if (err < 0)
This commit adds a functionality to multiplex PCM frames into isochronous packets and demultiplex PCM frames from isochronous packets for ALSA PCM applications.
Fireface 800 voluntarily maintains resources for tx isochronous communication. It performs reservation of isochronous channel and allocation/update of bandwidth in some cases below: - at a first request to allocation after bus resets - at requests to allocation when further bandwidth is required
When request is grant and the unit is prepared, read data from 0x0000801c0008 represents isochronous channel for tx stream, then the unit can handle requests to start communication. If driver send the request without checking the register, the unit takes panic to continue bus resets. The unit starts transmission of tx packets after receiving several rx packets from driver.
I note that the unit can process tx/rx packets and generate/record sound regardless of HOST LED.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff800.c | 116 ++++++++++++++++++++ sound/firewire/fireface/ff.c | 29 +++-- 2 files changed, 129 insertions(+), 16 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c index d24439734304..2acbf6039770 100644 --- a/sound/firewire/fireface/ff-protocol-ff800.c +++ b/sound/firewire/fireface/ff-protocol-ff800.c @@ -6,8 +6,122 @@ * 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; @@ -24,4 +138,6 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
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.c b/sound/firewire/fireface/ff.c index d486984c0e5b..f7a752930060 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -31,8 +31,7 @@ static void ff_card_free(struct snd_card *card) { struct snd_ff *ff = card->private_data;
- if (ff->spec->protocol->begin_session) - snd_ff_stream_destroy_duplex(ff); + snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff); }
@@ -57,11 +56,9 @@ static void do_registration(struct work_struct *work)
name_card(ff);
- if (ff->spec->protocol->begin_session) { - err = snd_ff_stream_init_duplex(ff); - if (err < 0) - goto error; - } + err = snd_ff_stream_init_duplex(ff); + if (err < 0) + goto error;
snd_ff_proc_init(ff);
@@ -69,15 +66,13 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error;
- if (ff->spec->protocol->begin_session) { - err = snd_ff_create_pcm_devices(ff); - if (err < 0) - goto error; + err = snd_ff_create_pcm_devices(ff); + if (err < 0) + goto error;
- err = snd_ff_create_hwdep_devices(ff); - if (err < 0) - goto error; - } + err = snd_ff_create_hwdep_devices(ff); + if (err < 0) + goto error;
err = snd_card_register(ff->card); if (err < 0) @@ -126,7 +121,7 @@ static void snd_ff_update(struct fw_unit *unit)
snd_ff_transaction_reregister(ff);
- if (ff->registered && ff->spec->protocol->begin_session) + if (ff->registered) snd_ff_stream_update_duplex(ff); }
@@ -152,6 +147,8 @@ static void snd_ff_remove(struct fw_unit *unit)
static const struct snd_ff_spec spec_ff800 = { .name = "Fireface800", + .pcm_capture_channels = {28, 20, 12}, + .pcm_playback_channels = {28, 20, 12}, .midi_in_ports = 1, .midi_out_ports = 1, .protocol = &snd_ff_protocol_ff800,
As a result of investigation for Fireface 800, 'struct snd_ff_spec.regs' is just for higher address to receive tx asynchronous packets of MIDI messages, thus it can be simplified.
This commit simplifies it.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-transaction.c | 4 ++-- sound/firewire/fireface/ff.c | 8 ++------ sound/firewire/fireface/ff.h | 9 ++------- 3 files changed, 6 insertions(+), 15 deletions(-)
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index d8768348067b..5f4ddfd55403 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -278,7 +278,7 @@ int snd_ff_transaction_reregister(struct snd_ff *ff) addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32); reg = cpu_to_le32(addr); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->regs[SND_FF_REG_TYPE_MIDI_HIGH_ADDR], + ff->spec->midi_high_addr, ®, sizeof(reg), 0); }
@@ -319,7 +319,7 @@ void snd_ff_transaction_unregister(struct snd_ff *ff) /* Release higher 4 bytes of address. */ reg = cpu_to_le32(0x00000000); snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->regs[SND_FF_REG_TYPE_MIDI_HIGH_ADDR], + ff->spec->midi_high_addr, ®, sizeof(reg), 0);
fw_core_remove_address_handler(&ff->async_handler); diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index f7a752930060..36575f4159d1 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -152,9 +152,7 @@ static const struct snd_ff_spec spec_ff800 = { .midi_in_ports = 1, .midi_out_ports = 1, .protocol = &snd_ff_protocol_ff800, - .regs = { - [SND_FF_REG_TYPE_MIDI_HIGH_ADDR] = 0x000200000320ull, - }, + .midi_high_addr = 0x000200000320ull, };
static const struct snd_ff_spec spec_ff400 = { @@ -164,9 +162,7 @@ static const struct snd_ff_spec spec_ff400 = { .midi_in_ports = 2, .midi_out_ports = 2, .protocol = &snd_ff_protocol_ff400, - .regs = { - [SND_FF_REG_TYPE_MIDI_HIGH_ADDR] = 0x0000801003f4ull, - }, + .midi_high_addr = 0x0000801003f4ull, };
static const struct ieee1394_device_id snd_ff_id_table[] = { diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 9fdda4fbdbba..7dfc7745a914 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -36,7 +36,7 @@ #define SND_FF_OUT_MIDI_PORTS 2
#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull -/* For block wriet request. */ +/* For block write request. */ #define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull #define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull
@@ -47,11 +47,6 @@ enum snd_ff_stream_mode { SND_FF_STREAM_MODE_COUNT, };
-enum snd_ff_reg_type { - SND_FF_REG_TYPE_MIDI_HIGH_ADDR = 0, - SND_FF_REG_TYPE_COUNT, -}; - struct snd_ff_protocol; struct snd_ff_spec { const char *const name; @@ -63,7 +58,7 @@ struct snd_ff_spec { unsigned int midi_out_ports;
const struct snd_ff_protocol *protocol; - u64 regs[SND_FF_REG_TYPE_COUNT]; + u64 midi_high_addr; };
struct snd_ff {
On Sun, 16 Dec 2018 09:32:28 +0100, Takashi Sakamoto wrote:
Hi,
My former patchset adds support for Fireface 800 with MIDI functionality only[1]. This patchset updates driver with packet streaming functionality. As a result, both of PCM frames/MIDI messages are available.
This patchset is based on my former patch to fix a bug to switch frame fetching mode:
[alsa-devel] [PATCH] ALSA: fireface: fix for state to fetch PCM frames http://mailman.alsa-project.org/pipermail/alsa-devel/2018-December/143150.ht...
Unfortunately, my initial commit of ALSA fireface driver in v4.12 kernel includes bugs to handle tx isochronous packets. When testing this patchset, please apply below patchset in advance:
[alsa-devel] [PATCH 0/3] ALSA: firewire-lib: fixes for processing packets without CIP header http://mailman.alsa-project.org/pipermail/alsa-devel/2018-December/143146.ht...
For testers, a remote branch including overall patches is available in my remote repository in github.com[2]. They can be backport to Linux kernel v4.17 or later.
I note that this driver can generate hissing noise long-periodically as I reported for Fireface 400[3]. The cause is not cleared yet.
[1] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-December/142826.ht... [2] https://github.com/takaswie/snd-firewire-improve/tree/topic/ff800-pcm [3] http://mailman.alsa-project.org/pipermail/alsa-devel/2015-December/102261.ht...
Takashi Sakamoto (5): ALSA: fireface: share helper function to switch fetching mode ALSA: fireface: code refactoring to handle multiplier mode ALSA: fireface: allocate isochronous resources in mode-specific implementation ALSA: fireface: add support for packet streaming on Fireface 800 ALSA: fireface: code refactoring to handle model-specific registers
Applied all five patches now. Thanks.
Takashi
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto