[alsa-devel] [PATCH 0/4] ALSA: dice: enable to handle several streams
Hi,
This patchset updates my previous RFT, and goes for merging. [alsa-devel] [RFT][PATCH 0/4] ALSA: dice: enabled to handle several streams http://mailman.alsa-project.org/pipermail/alsa-devel/2016-March/105387.html
For the previous one, Stefan Richter worked as a tester with his devices (Focusrite Saffire Pro 24 and 40), while current patchset includes changes related to userspace interface. Therefore, I don't add Tested-by.
In previous one, ALSA dice driver uses subdevice for userspace to access any PCM substreams. On the other hand, the design of PCM subdevice leaves a bit cumbersome for userspace to handle several subdevices simultaneously in one process. These PCM substreams on a batch of AMDTP streams and it's better for ALSA dice driver to enable one process to handle them with simpler way.
Based on this concept, this patchset adds PCM character devices, instead of PCM subdevice. Thus, userspace applications can utilize PCM substreams via individual PCM character devices. In configuration space of alsa-lib, via 'hw:0,0' and 'hw:0,1' if available, like:
$ arecord -l ... card 1: Pro26000f5a [Pro26-000f5a], device 0: DICE [Pro26-000f5a] Subdevices: 1/1 Subdevice #0: subdevice #0 card 1: Pro26000f5a [Pro26-000f5a], device 1: DICE [Pro26-000f5a] Subdevices: 1/1 Subdevice #0: subdevice #0
For the other parts, there's no code changes. Therefore, Stefan's result still has meanings.
Changes: * Add additional PCM character device instead of PCM subdevice * Correct string entries to force-pcm * Improve comments
Takashi Sakamoto (4): ALSA: dice: have two sets of isochronous resources/streams ALSA: dice: handle whole available isochronous streams ALSA: dice: handle several PCM substreams when any isochronous streams are available ALSA: dice: force to add two pcm devices for listed models
sound/firewire/dice/dice-midi.c | 8 +- sound/firewire/dice/dice-pcm.c | 147 +++++++++----- sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- sound/firewire/dice/dice.c | 41 ++++ sound/firewire/dice/dice.h | 33 +++- 5 files changed, 424 insertions(+), 203 deletions(-)
Currently ALSA dice driver handles a pair of isochronous resources for IEC 61883-1/6 packet streaming. While, according to some documents about ASICs named as 'Dice', several isochronous streams are available.
Here, I start to describe ASICs produced under 'Dice' name. * Dice II (designed by wavefront semiconductor, including TCAT's IP) * STD (with limited functionality of DTCP) * CP (with full functionality of DTCP) * TCD2210/2210-E (so-called 'Dice Mini') * TCD2220/2220-E (so-called 'Dice Jr.') * TCD3070-CH (so-called 'Dice III')
Some documents are public and we can see hardware design of them. We can find some articles about hardware internal register definitions (not registers exported to IEEE 1394 bus).
* DICE II User Guide * http://www.tctechnologies.tc/archive/downloads/dice_ii_user_guide.pdf * 6.1 AVS Audio Receivers * Table 6.1: AVS Audio Receiver Memory Map * ARX1-ARX4 * 6.2 AVS Audio Transmitters * Table 6.2: AVS Audio Transmitter Memory Map * ATX1, ATX2 * TCD22xx User Guide * http://www.tctechnologies.tc/downloads/tcd22xx_user_guide.pdf * 6.1 AVS Audio Receivers * Table 66: AVS Audio Receiver Memory Map * ARX1, ARX2 * 6/2 AVS Audio Transmitters * Table 67: AVS Audio Transmitter Memory Map * ATX1, ATX2 * DICE III * http://www.tctechnologies.tc/downloads/TCD3070-CH.pdf * Dual stream 63 channel transmitter/receiver
For Dice II and TCD22xx series, maximum 16 data channels are transferred in an AMDTP packet, while for Dice III, maximum 32 data channels are transferred.
According to the design of the series of these ASICs, this commit allows this driver to handle additional set of isochronous resources. For practical reason, two pair of isochronous resources are added. As of this commit, this driver still use a pair of the first isochronous resources.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-midi.c | 8 +++--- sound/firewire/dice/dice-pcm.c | 34 ++++++++++++++---------- sound/firewire/dice/dice-stream.c | 54 +++++++++++++++++++-------------------- sound/firewire/dice/dice.h | 31 +++++++++++++++++++--- 4 files changed, 79 insertions(+), 48 deletions(-)
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 2461311..a040617 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags);
if (up) - amdtp_am824_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream[0], substrm->number, substrm); else - amdtp_am824_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream[0], substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags); @@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags);
if (up) - amdtp_am824_midi_trigger(&dice->rx_stream, + amdtp_am824_midi_trigger(&dice->rx_stream[0], substrm->number, substrm); else - amdtp_am824_midi_trigger(&dice->rx_stream, + amdtp_am824_midi_trigger(&dice->rx_stream[0], substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags); diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index a5c9b58..e252949 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -22,7 +22,7 @@ static int limit_channels_and_rates(struct snd_dice *dice, * Retrieve current Multi Bit Linear Audio data channel and limit to * it. */ - if (stream == &dice->tx_stream) { + if (stream == &dice->tx_stream[0]) { err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { @@ -74,10 +74,10 @@ static int init_hw_info(struct snd_dice *dice,
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; - stream = &dice->tx_stream; + stream = &dice->tx_stream[0]; } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; - stream = &dice->rx_stream; + stream = &dice->rx_stream[0]; }
err = limit_channels_and_rates(dice, runtime, stream); @@ -122,6 +122,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0]; int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -135,7 +136,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); }
- amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0; } @@ -143,6 +144,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0]; int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -156,7 +158,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); }
- amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0; } @@ -196,26 +198,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream) static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0]; int err;
mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) - amdtp_stream_pcm_prepare(&dice->tx_stream); + amdtp_stream_pcm_prepare(stream);
return 0; } static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0]; int err;
mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) - amdtp_stream_pcm_prepare(&dice->rx_stream); + amdtp_stream_pcm_prepare(stream);
return err; } @@ -223,13 +227,14 @@ static int playback_prepare(struct snd_pcm_substream *substream) static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0];
switch (cmd) { case SNDRV_PCM_TRIGGER_START: - amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + amdtp_stream_pcm_trigger(stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: - amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + amdtp_stream_pcm_trigger(stream, NULL); break; default: return -EINVAL; @@ -240,13 +245,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd) static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0];
switch (cmd) { case SNDRV_PCM_TRIGGER_START: - amdtp_stream_pcm_trigger(&dice->rx_stream, substream); + amdtp_stream_pcm_trigger(stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: - amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); + amdtp_stream_pcm_trigger(stream, NULL); break; default: return -EINVAL; @@ -258,14 +264,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[0];
- return amdtp_stream_pcm_pointer(&dice->tx_stream); + return amdtp_stream_pcm_pointer(stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[0];
- return amdtp_stream_pcm_pointer(&dice->rx_stream); + return amdtp_stream_pcm_pointer(stream); }
int snd_dice_create_pcm(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index df035b1..15d581d 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -72,7 +72,7 @@ static void release_resources(struct snd_dice *dice,
/* Reset channel number */ channel = cpu_to_be32((u32)-1); - if (resources == &dice->tx_resources) + if (resources == &dice->tx_resources[0]) snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, &channel, sizeof(channel)); else @@ -96,7 +96,7 @@ static int keep_resources(struct snd_dice *dice,
/* Set channel number */ channel = cpu_to_be32(resources->channel); - if (resources == &dice->tx_resources) + if (resources == &dice->tx_resources[0]) err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, &channel, sizeof(channel)); else @@ -113,10 +113,10 @@ static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) amdtp_stream_pcm_abort(stream); amdtp_stream_stop(stream);
- if (stream == &dice->tx_stream) - release_resources(dice, &dice->tx_resources); + if (stream == &dice->tx_stream[0]) + release_resources(dice, &dice->tx_resources[0]); else - release_resources(dice, &dice->rx_resources); + release_resources(dice, &dice->rx_resources[0]); }
static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, @@ -128,12 +128,12 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, bool double_pcm_frames; int err;
- if (stream == &dice->tx_stream) { - resources = &dice->tx_resources; + if (stream == &dice->tx_stream[0]) { + resources = &dice->tx_resources[0]; err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { - resources = &dice->rx_resources; + resources = &dice->rx_resources[0]; err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, reg, sizeof(reg)); } @@ -200,8 +200,8 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (dice->substreams_counter == 0) goto end;
- master = &dice->rx_stream; - slave = &dice->tx_stream; + master = &dice->rx_stream[0]; + slave = &dice->tx_stream[0];
/* Some packet queueing errors. */ if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) @@ -275,8 +275,8 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
snd_dice_transaction_clear_enable(dice);
- stop_stream(dice, &dice->tx_stream); - stop_stream(dice, &dice->rx_stream); + stop_stream(dice, &dice->tx_stream[0]); + stop_stream(dice, &dice->rx_stream[0]); }
static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) @@ -285,11 +285,11 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) struct fw_iso_resources *resources; enum amdtp_stream_direction dir;
- if (stream == &dice->tx_stream) { - resources = &dice->tx_resources; + if (stream == &dice->tx_stream[0]) { + resources = &dice->tx_resources[0]; dir = AMDTP_IN_STREAM; } else { - resources = &dice->rx_resources; + resources = &dice->rx_resources[0]; dir = AMDTP_OUT_STREAM; }
@@ -315,10 +315,10 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) { struct fw_iso_resources *resources;
- if (stream == &dice->tx_stream) - resources = &dice->tx_resources; + if (stream == &dice->tx_stream[0]) + resources = &dice->tx_resources[0]; else - resources = &dice->rx_resources; + resources = &dice->rx_resources[0];
amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); @@ -330,13 +330,13 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
dice->substreams_counter = 0;
- err = init_stream(dice, &dice->tx_stream); + err = init_stream(dice, &dice->tx_stream[0]); if (err < 0) goto end;
- err = init_stream(dice, &dice->rx_stream); + err = init_stream(dice, &dice->rx_stream[0]); if (err < 0) - destroy_stream(dice, &dice->tx_stream); + destroy_stream(dice, &dice->tx_stream[0]); end: return err; } @@ -345,8 +345,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { snd_dice_transaction_clear_enable(dice);
- destroy_stream(dice, &dice->tx_stream); - destroy_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->tx_stream[0]); + destroy_stream(dice, &dice->rx_stream[0]);
dice->substreams_counter = 0; } @@ -363,11 +363,11 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) */ dice->global_enabled = false;
- stop_stream(dice, &dice->rx_stream); - stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream[0]); + stop_stream(dice, &dice->tx_stream[0]);
- fw_iso_resources_update(&dice->rx_resources); - fw_iso_resources_update(&dice->tx_resources); + fw_iso_resources_update(&dice->rx_resources[0]); + fw_iso_resources_update(&dice->tx_resources[0]); }
static void dice_lock_changed(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 423cdba..8fba87d 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -39,6 +39,29 @@ #include "../lib.h" #include "dice-interface.h"
+/* + * This module support maximum 2 pairs of tx/rx isochronous streams for + * our convinience. + * + * In documents for ASICs called with a name of 'DICE': + * - ASIC for DICE II: + * - Maximum 2 tx and 4 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD2210/2210-E (so-called 'Dice Mini'): + * - Maximum 2 tx and 2 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD2220/2220-E (so-called 'Dice Jr.') + * - 2 tx and 2 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD3070-CH (so-called 'Dice III') + * - Maximum 2 tx and 2 rx are supported. + * - A packet supports maximum 32 data channels. + * + * For the above, MIDI conformant data channel is just on the first isochronous + * stream. + */ +#define MAX_STREAMS 2 + struct snd_dice { struct snd_card *card; struct fw_unit *unit; @@ -67,10 +90,10 @@ struct snd_dice { wait_queue_head_t hwdep_wait;
/* For streaming */ - struct fw_iso_resources tx_resources; - struct fw_iso_resources rx_resources; - struct amdtp_stream tx_stream; - struct amdtp_stream rx_stream; + struct fw_iso_resources tx_resources[MAX_STREAMS]; + struct fw_iso_resources rx_resources[MAX_STREAMS]; + struct amdtp_stream tx_stream[MAX_STREAMS]; + struct amdtp_stream rx_stream[MAX_STREAMS]; bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter;
This commit enables ALSA dice driver to handle whole available streams.
In Dice, certain registers represent the number of available streams at current sampling transfer frequency for both directions. The parameters of each stream are represented in a block of register. This block is aligned sequentially. These streams start simultaneously by writing enable bit to a register.
This commit operates these registers when starting/stopping streams.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- 1 file changed, 255 insertions(+), 143 deletions(-)
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 15d581d..2077f18 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -65,85 +65,84 @@ static int ensure_phase_lock(struct snd_dice *dice) return 0; }
-static void release_resources(struct snd_dice *dice, - struct fw_iso_resources *resources) +static int get_register_params(struct snd_dice *dice, unsigned int params[4]) { - __be32 channel; - - /* Reset channel number */ - channel = cpu_to_be32((u32)-1); - if (resources == &dice->tx_resources[0]) - snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, sizeof(channel)); - else - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, sizeof(channel)); - - fw_iso_resources_free(resources); -} - -static int keep_resources(struct snd_dice *dice, - struct fw_iso_resources *resources, - unsigned int max_payload_bytes) -{ - __be32 channel; + __be32 reg[2]; int err;
- err = fw_iso_resources_allocate(resources, max_payload_bytes, - fw_parent_device(dice->unit)->max_speed); + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); if (err < 0) - goto end; + return err; + params[0] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + params[1] = be32_to_cpu(reg[1]) * 4;
- /* Set channel number */ - channel = cpu_to_be32(resources->channel); - if (resources == &dice->tx_resources[0]) - err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, sizeof(channel)); - else - err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, sizeof(channel)); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); if (err < 0) - release_resources(dice, resources); -end: - return err; + return err; + params[2] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + params[3] = be32_to_cpu(reg[1]) * 4; + + return 0; }
-static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static void release_resources(struct snd_dice *dice) { - amdtp_stream_pcm_abort(stream); - amdtp_stream_stop(stream); + unsigned int i; + + for (i = 0; i < MAX_STREAMS; i++) { + if (amdtp_stream_running(&dice->tx_stream[i])) { + amdtp_stream_pcm_abort(&dice->tx_stream[i]); + amdtp_stream_stop(&dice->tx_stream[i]); + } + if (amdtp_stream_running(&dice->rx_stream[i])) { + amdtp_stream_pcm_abort(&dice->rx_stream[i]); + amdtp_stream_stop(&dice->rx_stream[i]); + }
- if (stream == &dice->tx_stream[0]) - release_resources(dice, &dice->tx_resources[0]); - else - release_resources(dice, &dice->rx_resources[0]); + fw_iso_resources_free(&dice->tx_resources[i]); + fw_iso_resources_free(&dice->rx_resources[i]); + } }
-static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, - unsigned int rate) +static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int count, unsigned int size) { + __be32 reg; + unsigned int i; + + for (i = 0; i < count; i++) { + reg = cpu_to_be32((u32)-1); + if (dir == AMDTP_IN_STREAM) { + snd_dice_transaction_write_tx(dice, + size * i + TX_ISOCHRONOUS, + ®, sizeof(reg)); + } else { + snd_dice_transaction_write_rx(dice, + size * i + RX_ISOCHRONOUS, + ®, sizeof(reg)); + } + } +} + +static int keep_resources(struct snd_dice *dice, + enum amdtp_stream_direction dir, unsigned int index, + unsigned int rate, unsigned int pcm_chs, + unsigned int midi_ports) +{ + struct amdtp_stream *stream; struct fw_iso_resources *resources; - __be32 reg[2]; - unsigned int i, pcm_chs, midi_ports; bool double_pcm_frames; + unsigned int i; int err;
- if (stream == &dice->tx_stream[0]) { - resources = &dice->tx_resources[0]; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - reg, sizeof(reg)); + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources[0]; - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - reg, sizeof(reg)); + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; }
- if (err < 0) - goto end; - - pcm_chs = be32_to_cpu(reg[0]); - midi_ports = be32_to_cpu(reg[1]); - /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in * one data block of AMDTP packet. Thus sampling transfer frequency is @@ -163,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, double_pcm_frames); if (err < 0) - goto end; + return err;
if (double_pcm_frames) { pcm_chs /= 2; @@ -175,122 +174,208 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, } }
- err = keep_resources(dice, resources, - amdtp_stream_get_max_payload(stream)); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to keep isochronous resources\n"); - goto end; + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), + fw_parent_device(dice->unit)->max_speed); +} + +static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int rate, unsigned int count, + unsigned int size) +{ + __be32 reg[2]; + unsigned int i, pcm_chs, midi_ports; + struct amdtp_stream *streams; + struct fw_iso_resources *resources; + int err = 0; + + if (dir == AMDTP_IN_STREAM) { + streams = dice->tx_stream; + resources = dice->tx_resources; + } else { + streams = dice->rx_stream; + resources = dice->rx_resources; + } + + for (i = 0; i < count; i++) { + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_read_tx(dice, + size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); + } else { + err = snd_dice_transaction_read_rx(dice, + size * i + RX_NUMBER_AUDIO, + reg, sizeof(reg)); + } + if (err < 0) + return err; + pcm_chs = be32_to_cpu(reg[0]); + midi_ports = be32_to_cpu(reg[1]); + + err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports); + if (err < 0) + return err; + + reg[0] = cpu_to_be32(resources[i].channel); + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_write_tx(dice, + size * i + TX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } else { + err = snd_dice_transaction_write_rx(dice, + size * i + RX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } + if (err < 0) + return err; + + err = amdtp_stream_start(&streams[i], resources[i].channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; }
- err = amdtp_stream_start(stream, resources->channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - release_resources(dice, resources); -end: return err; }
+/* + * MEMO: After this function, there're two states of streams: + * - None streams are running. + * - All streams are running. + */ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { - struct amdtp_stream *master, *slave; unsigned int curr_rate; - int err = 0; + unsigned int i; + unsigned int reg_params[4]; + bool need_to_start; + int err;
if (dice->substreams_counter == 0) - goto end; - - master = &dice->rx_stream[0]; - slave = &dice->tx_stream[0]; + return -EIO;
- /* Some packet queueing errors. */ - if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) - stop_stream(dice, master); + err = get_register_params(dice, reg_params); + if (err < 0) + return err;
- /* Stop stream if rate is different. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); if (err < 0) { dev_err(&dice->unit->device, "fail to get sampling rate\n"); - goto end; + return err; } if (rate == 0) rate = curr_rate; - if (rate != curr_rate) { - err = -EINVAL; - goto end; + if (rate != curr_rate) + return -EINVAL; + + /* Judge to need to restart streams. */ + for (i = 0; i < MAX_STREAMS; i++) { + if (i < reg_params[0]) { + if (amdtp_streaming_error(&dice->tx_stream[i]) || + !amdtp_stream_running(&dice->tx_stream[i])) + break; + } + if (i < reg_params[2]) { + if (amdtp_streaming_error(&dice->rx_stream[i]) || + !amdtp_stream_running(&dice->rx_stream[i])) + break; + } } + need_to_start = (i < MAX_STREAMS);
- if (!amdtp_stream_running(master)) { - stop_stream(dice, slave); + if (need_to_start) { + /* Stop transmission. */ snd_dice_transaction_clear_enable(dice); + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + release_resources(dice);
err = ensure_phase_lock(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to ensure phase lock\n"); - goto end; + return err; }
/* Start both streams. */ - err = start_stream(dice, master, rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to start AMDTP master stream\n"); - goto end; - } - err = start_stream(dice, slave, rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to start AMDTP slave stream\n"); - stop_stream(dice, master); - goto end; - } + err = start_streams(dice, AMDTP_IN_STREAM, rate, reg_params[0], + reg_params[1]); + if (err < 0) + goto error; + err = start_streams(dice, AMDTP_OUT_STREAM, rate, reg_params[2], + reg_params[3]); + if (err < 0) + goto error; + err = snd_dice_transaction_set_enable(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to enable interface\n"); - stop_stream(dice, master); - stop_stream(dice, slave); - goto end; + goto error; }
- /* Wait first callbacks */ - if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { - snd_dice_transaction_clear_enable(dice); - stop_stream(dice, master); - stop_stream(dice, slave); - err = -ETIMEDOUT; + for (i = 0; i < MAX_STREAMS; i++) { + if ((i < reg_params[0] && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < reg_params[2] && + !amdtp_stream_wait_callback(&dice->rx_stream[i], + CALLBACK_TIMEOUT))) { + err = -ETIMEDOUT; + goto error; + } } } -end: + + return err; +error: + snd_dice_transaction_clear_enable(dice); + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], reg_params[3]); + release_resources(dice); return err; }
+/* + * MEMO: After this function, there're two states of streams: + * - None streams are running. + * - All streams are running. + */ void snd_dice_stream_stop_duplex(struct snd_dice *dice) { + unsigned int reg_params[4]; + if (dice->substreams_counter > 0) return;
snd_dice_transaction_clear_enable(dice);
- stop_stream(dice, &dice->tx_stream[0]); - stop_stream(dice, &dice->rx_stream[0]); + if (get_register_params(dice, reg_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + } + + release_resources(dice); }
-static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int index) { - int err; + struct amdtp_stream *stream; struct fw_iso_resources *resources; - enum amdtp_stream_direction dir; + int err;
- if (stream == &dice->tx_stream[0]) { - resources = &dice->tx_resources[0]; - dir = AMDTP_IN_STREAM; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources[0]; - dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; }
err = fw_iso_resources_init(resources, dice->unit); @@ -311,14 +396,20 @@ end: * This function should be called before starting streams or after stopping * streams. */ -static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static void destroy_stream(struct snd_dice *dice, + enum amdtp_stream_direction dir, + unsigned int index) { + struct amdtp_stream *stream; struct fw_iso_resources *resources;
- if (stream == &dice->tx_stream[0]) - resources = &dice->tx_resources[0]; - else - resources = &dice->rx_resources[0]; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; + } else { + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; + }
amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); @@ -326,33 +417,53 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
int snd_dice_stream_init_duplex(struct snd_dice *dice) { - int err; + int i, err;
- dice->substreams_counter = 0; - - err = init_stream(dice, &dice->tx_stream[0]); - if (err < 0) - goto end; + for (i = 0; i < MAX_STREAMS; i++) { + err = init_stream(dice, AMDTP_IN_STREAM, i); + if (err < 0) { + for (; i >= 0; i--) + destroy_stream(dice, AMDTP_OUT_STREAM, i); + goto end; + } + }
- err = init_stream(dice, &dice->rx_stream[0]); - if (err < 0) - destroy_stream(dice, &dice->tx_stream[0]); + for (i = 0; i < MAX_STREAMS; i++) { + err = init_stream(dice, AMDTP_OUT_STREAM, i); + if (err < 0) { + for (; i >= 0; i--) + destroy_stream(dice, AMDTP_OUT_STREAM, i); + for (i = 0; i < MAX_STREAMS; i++) + destroy_stream(dice, AMDTP_IN_STREAM, i); + break; + } + } end: return err; }
void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { + unsigned int reg_params[4]; + snd_dice_transaction_clear_enable(dice);
- destroy_stream(dice, &dice->tx_stream[0]); - destroy_stream(dice, &dice->rx_stream[0]); + if (get_register_params(dice, reg_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + } + + release_resources(dice);
dice->substreams_counter = 0; }
void snd_dice_stream_update_duplex(struct snd_dice *dice) { + unsigned int reg_params[4]; + /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -363,11 +474,12 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) */ dice->global_enabled = false;
- stop_stream(dice, &dice->rx_stream[0]); - stop_stream(dice, &dice->tx_stream[0]); - - fw_iso_resources_update(&dice->rx_resources[0]); - fw_iso_resources_update(&dice->tx_resources[0]); + if (get_register_params(dice, reg_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, reg_params[0], + reg_params[1]); + stop_streams(dice, AMDTP_OUT_STREAM, reg_params[2], + reg_params[3]); + } }
static void dice_lock_changed(struct snd_dice *dice)
On Mon, 07 Mar 2016 14:35:43 +0100, Takashi Sakamoto wrote:
This commit enables ALSA dice driver to handle whole available streams.
In Dice, certain registers represent the number of available streams at current sampling transfer frequency for both directions. The parameters of each stream are represented in a block of register. This block is aligned sequentially. These streams start simultaneously by writing enable bit to a register.
This commit operates these registers when starting/stopping streams.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp
sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- 1 file changed, 255 insertions(+), 143 deletions(-)
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 15d581d..2077f18 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -65,85 +65,84 @@ static int ensure_phase_lock(struct snd_dice *dice) return 0; }
-static void release_resources(struct snd_dice *dice,
struct fw_iso_resources *resources)
+static int get_register_params(struct snd_dice *dice, unsigned int params[4]) {
- __be32 channel;
- /* Reset channel number */
- channel = cpu_to_be32((u32)-1);
- if (resources == &dice->tx_resources[0])
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
&channel, sizeof(channel));
- else
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
&channel, sizeof(channel));
- fw_iso_resources_free(resources);
-}
-static int keep_resources(struct snd_dice *dice,
struct fw_iso_resources *resources,
unsigned int max_payload_bytes)
-{
- __be32 channel;
- __be32 reg[2]; int err;
- err = fw_iso_resources_allocate(resources, max_payload_bytes,
fw_parent_device(dice->unit)->max_speed);
- err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); if (err < 0)
goto end;
return err;
- params[0] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
- params[1] = be32_to_cpu(reg[1]) * 4;
- /* Set channel number */
- channel = cpu_to_be32(resources->channel);
- if (resources == &dice->tx_resources[0])
err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
&channel, sizeof(channel));
- else
err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
&channel, sizeof(channel));
- err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); if (err < 0)
release_resources(dice, resources);
-end:
- return err;
return err;
- params[2] = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
- params[3] = be32_to_cpu(reg[1]) * 4;
These "parameters" are not necessarily an array since each field has a strict definition. A more readable code would be to define a struct instead.
thanks,
Takashi
In former commits, ALSA dice driver can handle available isochronous streams. This commit adds support for several PCM substreams on the streams.
The additional PCM substreams are available via another ALSA PCM character devices so that one ALSA PCM application can handle them without cumbersome operations. For example, two PCM substreams are available on each stream, two ALSA character devices are added for them. In configuration space of alsa-lib, it's represented with 'hw:0,0' and 'hw:0,1'.
The PCM substreams are constraint to parameters of the corresponding streams. If the PCM substreams are unavailable for some reasons, open(2) to ALSA PCM character device returns error and reports ENXIO.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-pcm.c | 121 +++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 46 deletions(-)
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index e252949..05e2e18 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -11,28 +11,34 @@
static int limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, - struct amdtp_stream *stream) + enum amdtp_stream_direction dir, + unsigned int index, unsigned int size) { struct snd_pcm_hardware *hw = &runtime->hw; + struct amdtp_stream *stream; unsigned int rate; - __be32 reg[2]; + __be32 reg; int err;
/* * Retrieve current Multi Bit Linear Audio data channel and limit to * it. */ - if (stream == &dice->tx_stream[0]) { - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - reg, sizeof(reg)); + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + err = snd_dice_transaction_read_tx(dice, + size * index + TX_NUMBER_AUDIO, + ®, sizeof(reg)); } else { - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - reg, sizeof(reg)); + stream = &dice->rx_stream[index]; + err = snd_dice_transaction_read_rx(dice, + size * index + RX_NUMBER_AUDIO, + ®, sizeof(reg)); } if (err < 0) return err;
- hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]); + hw->channels_min = hw->channels_max = be32_to_cpu(reg);
/* Retrieve current sampling transfer frequency and limit to it. */ err = snd_dice_transaction_get_rate(dice, &rate); @@ -62,7 +68,10 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + enum amdtp_stream_direction dir; struct amdtp_stream *stream; + __be32 reg[2]; + unsigned int count, size; int err;
hw->info = SNDRV_PCM_INFO_MMAP | @@ -74,13 +83,28 @@ static int init_hw_info(struct snd_dice *dice,
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; - stream = &dice->tx_stream[0]; + dir = AMDTP_IN_STREAM; + stream = &dice->tx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, + sizeof(reg)); } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; - stream = &dice->rx_stream[0]; + dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, + sizeof(reg)); }
- err = limit_channels_and_rates(dice, runtime, stream); + if (err < 0) + return err; + + count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + if (substream->pcm->device >= count) + return -ENXIO; + + size = be32_to_cpu(reg[1]) * 4; + err = limit_channels_and_rates(dice, substream->runtime, dir, + substream->pcm->device, size); if (err < 0) return err; limit_period_and_buffer(hw); @@ -122,7 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -144,7 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -198,7 +222,7 @@ static int playback_hw_free(struct snd_pcm_substream *substream) static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err;
mutex_lock(&dice->mutex); @@ -212,7 +236,7 @@ static int capture_prepare(struct snd_pcm_substream *substream) static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err;
mutex_lock(&dice->mutex); @@ -227,7 +251,7 @@ static int playback_prepare(struct snd_pcm_substream *substream) static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -245,7 +269,7 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd) static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -264,14 +288,14 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[0]; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
return amdtp_stream_pcm_pointer(stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[0]; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
return amdtp_stream_pcm_pointer(stream); } @@ -304,40 +328,45 @@ int snd_dice_create_pcm(struct snd_dice *dice) }; __be32 reg; struct snd_pcm *pcm; - unsigned int capture, playback; + unsigned int i, max_capture, max_playback, capture, playback; int err;
- /* - * Check whether PCM substreams are required. - * - * TODO: in the case that any PCM substreams are not avail at a certain - * sampling transfer frequency? - */ - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - ®, sizeof(reg)); - if (err < 0) - return err; - if (be32_to_cpu(reg) > 0) - capture = 1; - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - ®, sizeof(reg)); + /* Check whether PCM substreams are required. */ + max_capture = max_playback = 0; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, sizeof(reg)); if (err < 0) return err; - if (be32_to_cpu(reg) > 0) - playback = 1; + max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
- err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, sizeof(reg)); if (err < 0) return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); - - if (capture > 0) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); - - if (playback > 0) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + + for (i = 0; i < MAX_STREAMS; i++) { + capture = playback = 0; + if (i < max_capture) + capture = 1; + if (i < max_playback) + playback = 1; + if (capture == 0 && playback == 0) + break; + + err = snd_pcm_new(dice->card, "DICE", i, playback, capture, + &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + + if (capture > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &capture_ops); + + if (playback > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &playback_ops); + }
return 0; }
Some models reduce the number of available isochronous streams for higher sampling transfer frequency. Such models bring an issue about how to add PCM substreams. When at lower sampling transfer frequency, the models reports whole available streams, thus this driver can add enough number of PCM substreams at probing time. On the other hand, at higher sampling transfer frequency, this driver can just add reduced number of PCM substreams. After probed, even if the sampling transfer frequency is changed to lower rate, fewer PCM substreams are actually available. This is inconvenience.
For the reason, this commit adds a list so that this driver assume models on the list to have two pairs of PCM substreams. This list keeps the name of model in which the number of available streams differs depending on sampling transfer frequency.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-pcm.c | 24 +++++++++++++++--------- sound/firewire/dice/dice.c | 41 +++++++++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice.h | 2 ++ 3 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 05e2e18..4aa0249 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -332,16 +332,22 @@ int snd_dice_create_pcm(struct snd_dice *dice) int err;
/* Check whether PCM substreams are required. */ - max_capture = max_playback = 0; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, sizeof(reg)); - if (err < 0) - return err; - max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + if (dice->force_two_pcms) { + max_capture = max_playback = 2; + } else { + max_capture = max_playback = 0; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, + sizeof(reg)); + if (err < 0) + return err; + max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
- err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, sizeof(reg)); - if (err < 0) - return err; - max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, + sizeof(reg)); + if (err < 0) + return err; + max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + }
for (i = 0; i < MAX_STREAMS; i++) { capture = playback = 0; diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index f7303a6..8b64aef 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2");
#define OUI_WEISS 0x001c6a #define OUI_LOUD 0x000ff2 +#define OUI_FOCUSRITE 0x00130e +#define OUI_TCELECTRONIC 0x001486
#define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 @@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2");
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
+/* + * Some models support several isochronous channels, while these streams are not + * always available. In this case, add the model name to this list. + */ +static bool force_two_pcm_support(struct fw_unit *unit) +{ + const char *const models[] = { + /* TC Electronic models. */ + "StudioKonnekt48", + /* Focusrite models. */ + "SAFFIRE_PRO_40", + "LIQUID_SAFFIRE_56", + "SAFFIRE_PRO_40_1", + }; + char model[32]; + unsigned int i; + int err; + + err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model)); + if (err < 0) + return false; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + if (strcmp(models[i], model) == 0) + break; + } + + return i < ARRAY_SIZE(models); +} + static int check_dice_category(struct fw_unit *unit) { struct fw_device *device = fw_parent_device(unit); @@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit) break; } } + + if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) { + if (force_two_pcm_support(unit)) + return 0; + } + if (vendor == OUI_WEISS) category = WEISS_CATEGORY_ID; else if (vendor == OUI_LOUD) @@ -150,6 +188,9 @@ static void do_registration(struct work_struct *work) if (err < 0) return;
+ if (force_two_pcm_support(dice->unit)) + dice->force_two_pcms = true; + err = snd_dice_transaction_init(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 8fba87d..e6c0785 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -97,6 +97,8 @@ struct snd_dice { bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; + + bool force_two_pcms; };
enum snd_dice_addr_type {
Hi,
On mar 7 2016 22:35, Takashi Sakamoto wrote:
Hi,
This patchset updates my previous RFT, and goes for merging. [alsa-devel] [RFT][PATCH 0/4] ALSA: dice: enabled to handle several streams http://mailman.alsa-project.org/pipermail/alsa-devel/2016-March/105387.html
For the previous one, Stefan Richter worked as a tester with his devices (Focusrite Saffire Pro 24 and 40), while current patchset includes changes related to userspace interface. Therefore, I don't add Tested-by.
In previous one, ALSA dice driver uses subdevice for userspace to access any PCM substreams. On the other hand, the design of PCM subdevice leaves a bit cumbersome for userspace to handle several subdevices simultaneously in one process. These PCM substreams on a batch of AMDTP streams and it's better for ALSA dice driver to enable one process to handle them with simpler way.
Based on this concept, this patchset adds PCM character devices, instead of PCM subdevice. Thus, userspace applications can utilize PCM substreams via individual PCM character devices. In configuration space of alsa-lib, via 'hw:0,0' and 'hw:0,1' if available, like:
$ arecord -l ... card 1: Pro26000f5a [Pro26-000f5a], device 0: DICE [Pro26-000f5a] Subdevices: 1/1 Subdevice #0: subdevice #0 card 1: Pro26000f5a [Pro26-000f5a], device 1: DICE [Pro26-000f5a] Subdevices: 1/1 Subdevice #0: subdevice #0
For the other parts, there's no code changes. Therefore, Stefan's result still has meanings.
Changes:
- Add additional PCM character device instead of PCM subdevice
- Correct string entries to force-pcm
- Improve comments
Takashi Sakamoto (4): ALSA: dice: have two sets of isochronous resources/streams ALSA: dice: handle whole available isochronous streams ALSA: dice: handle several PCM substreams when any isochronous streams are available ALSA: dice: force to add two pcm devices for listed models
sound/firewire/dice/dice-midi.c | 8 +- sound/firewire/dice/dice-pcm.c | 147 +++++++++----- sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- sound/firewire/dice/dice.c | 41 ++++ sound/firewire/dice/dice.h | 33 +++- 5 files changed, 424 insertions(+), 203 deletions(-)
I strongly request to merge this patchset to linux-next for 4.6 kernel. For already supported models, this patchset basically adds no changes except for actual codes. Therefore few possibilities of regressions.
Regards
Takashi Sakamoto
On Wed, 09 Mar 2016 14:15:03 +0100, Takashi Sakamoto wrote:
Hi,
On mar 7 2016 22:35, Takashi Sakamoto wrote:
Hi,
This patchset updates my previous RFT, and goes for merging. [alsa-devel] [RFT][PATCH 0/4] ALSA: dice: enabled to handle several streams http://mailman.alsa-project.org/pipermail/alsa-devel/2016-March/105387.html
For the previous one, Stefan Richter worked as a tester with his devices (Focusrite Saffire Pro 24 and 40), while current patchset includes changes related to userspace interface. Therefore, I don't add Tested-by.
In previous one, ALSA dice driver uses subdevice for userspace to access any PCM substreams. On the other hand, the design of PCM subdevice leaves a bit cumbersome for userspace to handle several subdevices simultaneously in one process. These PCM substreams on a batch of AMDTP streams and it's better for ALSA dice driver to enable one process to handle them with simpler way.
Based on this concept, this patchset adds PCM character devices, instead of PCM subdevice. Thus, userspace applications can utilize PCM substreams via individual PCM character devices. In configuration space of alsa-lib, via 'hw:0,0' and 'hw:0,1' if available, like:
$ arecord -l ... card 1: Pro26000f5a [Pro26-000f5a], device 0: DICE [Pro26-000f5a] Subdevices: 1/1 Subdevice #0: subdevice #0 card 1: Pro26000f5a [Pro26-000f5a], device 1: DICE [Pro26-000f5a] Subdevices: 1/1 Subdevice #0: subdevice #0
For the other parts, there's no code changes. Therefore, Stefan's result still has meanings.
Changes:
- Add additional PCM character device instead of PCM subdevice
- Correct string entries to force-pcm
- Improve comments
Takashi Sakamoto (4): ALSA: dice: have two sets of isochronous resources/streams ALSA: dice: handle whole available isochronous streams ALSA: dice: handle several PCM substreams when any isochronous streams are available ALSA: dice: force to add two pcm devices for listed models
sound/firewire/dice/dice-midi.c | 8 +- sound/firewire/dice/dice-pcm.c | 147 +++++++++----- sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- sound/firewire/dice/dice.c | 41 ++++ sound/firewire/dice/dice.h | 33 +++- 5 files changed, 424 insertions(+), 203 deletions(-)
I strongly request to merge this patchset to linux-next for 4.6 kernel. For already supported models, this patchset basically adds no changes except for actual codes. Therefore few possibilities of regressions.
I expected some tested-by tags or such, but OK, it's fine to merge without that. There are minor nitpicks, but they can be improved later.
Now applied all patches.
thanks,
Takashi
On Mar 09 Takashi Iwai wrote:
On Wed, 09 Mar 2016 14:15:03 +0100, Takashi Sakamoto wrote:
On mar 7 2016 22:35, Takashi Sakamoto wrote:
Takashi Sakamoto (4): ALSA: dice: have two sets of isochronous resources/streams ALSA: dice: handle whole available isochronous streams ALSA: dice: handle several PCM substreams when any isochronous streams are available ALSA: dice: force to add two pcm devices for listed models
sound/firewire/dice/dice-midi.c | 8 +- sound/firewire/dice/dice-pcm.c | 147 +++++++++----- sound/firewire/dice/dice-stream.c | 398 ++++++++++++++++++++++++-------------- sound/firewire/dice/dice.c | 41 ++++ sound/firewire/dice/dice.h | 33 +++- 5 files changed, 424 insertions(+), 203 deletions(-)
I strongly request to merge this patchset to linux-next for 4.6 kernel. For already supported models, this patchset basically adds no changes except for actual codes. Therefore few possibilities of regressions.
I expected some tested-by tags or such, but OK, it's fine to merge without that. There are minor nitpicks, but they can be improved later.
Now applied all patches.
I now tested this version of these four patches too, in the same way as the previous version from March 5. kernel: v4.5-rc6 plus ALSA: dice: limit to current sampling transfer frequency ALSA: dice: limit stream to current sampling transfer frequency. ALSA: dice: add MIDI ports according to current number of MIDI substreams ALSA: dice: get the number of MBLA data channel at opening PCM substream ALSA: dice: purge generating channel cache ALSA: dice: ensure phase lock before starting streaming ALSA: dice: change notification mask to detect lock status change ALSA: dice: old firmware optimization for Dice notification ALSA: dice: drop duplex streams synchronization to transfer own time stamps cherry-picked from tiwai/sound.git, and current 4 patches applied on top. Host: AMD64, Texas Instruments XIO2213B FireWire adapter. Audio devices tested: Focusrite Saffire PRO 24 and PRO 40. Again no regression with PRO 24 or PRO 40, previously inaccessible higher channels of the PRO 40 are now accessible (now as devices 0 and 1 on the respective ALSA card), older muting quirk of the PRO 40 at 88.2/96 kHz still happens with or without the 9+4 dice patches.
participants (3)
-
Stefan Richter
-
Takashi Iwai
-
Takashi Sakamoto