diff options
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/bebob/bebob.c | 21 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob.h | 5 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_midi.c | 16 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_pcm.c | 28 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_stream.c | 72 | ||||
-rw-r--r-- | sound/firewire/dice/dice-midi.c | 31 | ||||
-rw-r--r-- | sound/firewire/dice/dice-pcm.c | 278 | ||||
-rw-r--r-- | sound/firewire/dice/dice-stream.c | 468 | ||||
-rw-r--r-- | sound/firewire/dice/dice-transaction.c | 56 | ||||
-rw-r--r-- | sound/firewire/dice/dice.c | 108 | ||||
-rw-r--r-- | sound/firewire/dice/dice.h | 41 | ||||
-rw-r--r-- | sound/firewire/digi00x/amdtp-dot.c | 2 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks.c | 3 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_stream.c | 6 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw-scs1x.c | 35 | ||||
-rw-r--r-- | sound/firewire/tascam/tascam-transaction.c | 6 | ||||
-rw-r--r-- | sound/firewire/tascam/tascam.c | 12 | ||||
-rw-r--r-- | sound/firewire/tascam/tascam.h | 4 |
18 files changed, 601 insertions, 591 deletions
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 091290d1f3ea..3e4e0756e3fe 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -300,6 +300,22 @@ error: return err; } +/* + * This driver doesn't update streams in bus reset handler. + * + * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with + * discontinued counter at bus reset. This discontinuity is immediately + * detected in packet streaming layer, then it sets XRUN to PCM substream. + * + * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation. + * Then, they can recover the PCM substream by executing ioctl(2) with + * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers + * restart packet streaming. + * + * The above processing may be executed before this bus-reset handler is + * executed. When this handler updates streams with current isochronous + * channels, the streams already have the current ones. + */ static void bebob_update(struct fw_unit *unit) { @@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit) return; fcp_bus_reset(bebob->unit); - snd_bebob_stream_update_duplex(bebob); if (bebob->deferred_registration) { if (snd_card_register(bebob->card) < 0) { @@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit) if (bebob == NULL) return; - /* Awake bus-reset waiters. */ - if (!completion_done(&bebob->bus_reset)) - complete_all(&bebob->bus_reset); - /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(bebob->card); } diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 4d8fcc78e747..b50bb33d9d46 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -88,8 +88,6 @@ struct snd_bebob { unsigned int midi_input_ports; unsigned int midi_output_ports; - /* for bus reset quirk */ - struct completion bus_reset; bool connected; struct amdtp_stream *master; @@ -97,7 +95,7 @@ struct snd_bebob { struct amdtp_stream rx_stream; struct cmp_connection out_conn; struct cmp_connection in_conn; - atomic_t substreams_counter; + unsigned int substreams_counter; struct snd_bebob_stream_formation tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; @@ -219,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); -void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); void snd_bebob_stream_lock_changed(struct snd_bebob *bebob); diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 90d95be499b0..868eb0decbec 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; - atomic_inc(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; err = snd_bebob_stream_start_duplex(bebob, 0); + mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); end: @@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; - atomic_inc(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; err = snd_bebob_stream_start_duplex(bebob, 0); + mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); end: @@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; - atomic_dec(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); snd_bebob_stream_lock_release(bebob); return 0; @@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; - atomic_dec(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); snd_bebob_stream_lock_release(bebob); return 0; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index ef224d6f5c24..5d7b9343fa85 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - atomic_inc(&bebob->substreams_counter); + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; + mutex_unlock(&bebob->mutex); + } amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); @@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - atomic_inc(&bebob->substreams_counter); + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; + mutex_unlock(&bebob->mutex); + } amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); @@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - atomic_dec(&bebob->substreams_counter); + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; + mutex_unlock(&bebob->mutex); + } snd_bebob_stream_stop_duplex(bebob); @@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - atomic_dec(&bebob->substreams_counter); + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; + mutex_unlock(&bebob->mutex); + } snd_bebob_stream_stop_duplex(bebob); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 926e5dcbb66a..77cbb02bff34 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -47,14 +47,16 @@ static const unsigned int bridgeco_freq_table[] = { [6] = 0x07, }; -static unsigned int -get_formation_index(unsigned int rate) +static int +get_formation_index(unsigned int rate, unsigned int *index) { unsigned int i; for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) { - if (snd_bebob_rate_table[i] == rate) - return i; + if (snd_bebob_rate_table[i] == rate) { + *index = i; + return 0; + } } return -EINVAL; } @@ -425,7 +427,9 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) goto end; /* confirm params for both streams */ - index = get_formation_index(rate); + err = get_formation_index(rate, &index); + if (err < 0) + goto end; pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; err = amdtp_am824_set_parameters(&bebob->tx_stream, rate, @@ -545,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) destroy_both_connections(bebob); goto end; } - /* See comments in next function */ - init_completion(&bebob->bus_reset); + bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; /* @@ -584,29 +587,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) struct amdtp_stream *master, *slave; enum cip_flags sync_mode; unsigned int curr_rate; - bool updated = false; int err = 0; - /* - * Normal BeBoB firmware has a quirk at bus reset to transmits packets - * with discontinuous value in dbc field. - * - * This 'struct completion' is used to call .update() at first to update - * connections/streams. Next following codes handle streaming error. - */ - if (amdtp_streaming_error(&bebob->tx_stream)) { - if (completion_done(&bebob->bus_reset)) - reinit_completion(&bebob->bus_reset); - - updated = (wait_for_completion_interruptible_timeout( - &bebob->bus_reset, - msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0); - } - - mutex_lock(&bebob->mutex); - /* Need no substreams */ - if (atomic_read(&bebob->substreams_counter) == 0) + if (bebob->substreams_counter == 0) goto end; err = get_sync_mode(bebob, &sync_mode); @@ -638,8 +622,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) amdtp_stream_stop(master); if (amdtp_streaming_error(slave)) amdtp_stream_stop(slave); - if (!updated && - !amdtp_stream_running(master) && !amdtp_stream_running(slave)) + if (!amdtp_stream_running(master) && !amdtp_stream_running(slave)) break_both_connections(bebob); /* stop streams if rate is different */ @@ -737,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) } } end: - mutex_unlock(&bebob->mutex); return err; } @@ -753,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) master = &bebob->tx_stream; } - mutex_lock(&bebob->mutex); - - if (atomic_read(&bebob->substreams_counter) == 0) { + if (bebob->substreams_counter == 0) { amdtp_stream_pcm_abort(master); amdtp_stream_stop(master); @@ -764,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) break_both_connections(bebob); } - - mutex_unlock(&bebob->mutex); -} - -void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) -{ - /* vs. XRUN recovery due to discontinuity at bus reset */ - mutex_lock(&bebob->mutex); - - if ((cmp_connection_update(&bebob->in_conn) < 0) || - (cmp_connection_update(&bebob->out_conn) < 0)) { - amdtp_stream_pcm_abort(&bebob->rx_stream); - amdtp_stream_pcm_abort(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_stop(&bebob->tx_stream); - break_both_connections(bebob); - } else { - amdtp_stream_update(&bebob->rx_stream); - amdtp_stream_update(&bebob->tx_stream); - } - - /* wake up stream_start_duplex() */ - if (!completion_done(&bebob->bus_reset)) - complete_all(&bebob->bus_reset); - - mutex_unlock(&bebob->mutex); } /* diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 151b09f240f2..a040617505a7 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); @@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice, int snd_dice_create_midi(struct snd_dice *dice) { + __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; - unsigned int i, midi_in_ports, midi_out_ports; + unsigned int midi_in_ports, midi_out_ports; int err; - midi_in_ports = midi_out_ports = 0; - for (i = 0; i < 3; i++) { - midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports); - midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports); - } + /* + * Use the number of MIDI conformant data channel at current sampling + * transfer frequency. + */ + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI, + ®, sizeof(reg)); + if (err < 0) + return err; + midi_in_ports = be32_to_cpu(reg); + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI, + ®, sizeof(reg)); + if (err < 0) + return err; + midi_out_ports = be32_to_cpu(reg); if (midi_in_ports + midi_out_ports == 0) return 0; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 9b3431999fc8..4aa0249826fd 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -9,99 +9,46 @@ #include "dice.h" -static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_substream *substream = rule->private; - struct snd_dice *dice = substream->private_data; - - const struct snd_interval *c = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *r = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - pcm_channels = dice->tx_channels; - else - pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(c, pcm_channels[mode])) - continue; - - rates.min = min(rates.min, rate); - rates.max = max(rates.max, rate); - } - - return snd_interval_refine(r, &rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_substream *substream = rule->private; - struct snd_dice *dice = substream->private_data; - - const struct snd_interval *r = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *c = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - pcm_channels = dice->tx_channels; - else - pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(r, rate)) - continue; - - channels.min = min(channels.min, pcm_channels[mode]); - channels.max = max(channels.max, pcm_channels[mode]); - } - - return snd_interval_refine(c, &channels); -} - -static void limit_channels_and_rates(struct snd_dice *dice, - struct snd_pcm_runtime *runtime, - unsigned int *pcm_channels) +static int limit_channels_and_rates(struct snd_dice *dice, + struct snd_pcm_runtime *runtime, + enum amdtp_stream_direction dir, + unsigned int index, unsigned int size) { struct snd_pcm_hardware *hw = &runtime->hw; - unsigned int i, rate, mode; + struct amdtp_stream *stream; + unsigned int rate; + __be32 reg; + int err; - hw->channels_min = UINT_MAX; - hw->channels_max = 0; + /* + * Retrieve current Multi Bit Linear Audio data channel and limit to + * it. + */ + 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 { + stream = &dice->rx_stream[index]; + err = snd_dice_transaction_read_rx(dice, + size * index + RX_NUMBER_AUDIO, + ®, sizeof(reg)); + } + if (err < 0) + return err; - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - hw->rates |= snd_pcm_rate_to_rate_bit(rate); + hw->channels_min = hw->channels_max = be32_to_cpu(reg); - if (pcm_channels[mode] == 0) - continue; - hw->channels_min = min(hw->channels_min, pcm_channels[mode]); - hw->channels_max = max(hw->channels_max, pcm_channels[mode]); - } + /* Retrieve current sampling transfer frequency and limit to it. */ + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + return err; + hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); + + return 0; } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) @@ -121,8 +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; - unsigned int *pcm_channels; + __be32 reg[2]; + unsigned int count, size; int err; hw->info = SNDRV_PCM_INFO_MMAP | @@ -134,38 +83,38 @@ 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; - pcm_channels = dice->tx_channels; + 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; - pcm_channels = dice->rx_channels; + dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, + sizeof(reg)); } - limit_channels_and_rates(dice, runtime, pcm_channels); - limit_period_and_buffer(hw); - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, substream, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) - goto end; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, substream, - SNDRV_PCM_HW_PARAM_RATE, -1); + 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) - goto end; + return err; + limit_period_and_buffer(hw); - err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); -end: - return err; + return amdtp_am824_add_pcm_hw_constraints(stream, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - unsigned int source, rate; - bool internal; int err; err = snd_dice_stream_lock_try(dice); @@ -176,39 +125,6 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto err_locked; - switch (source) { - case CLOCK_SOURCE_AES1: - case CLOCK_SOURCE_AES2: - case CLOCK_SOURCE_AES3: - case CLOCK_SOURCE_AES4: - case CLOCK_SOURCE_AES_ANY: - case CLOCK_SOURCE_ADAT: - case CLOCK_SOURCE_TDIF: - case CLOCK_SOURCE_WC: - internal = false; - break; - default: - internal = true; - break; - } - - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ - if (!internal || - amdtp_stream_pcm_running(&dice->tx_stream) || - amdtp_stream_pcm_running(&dice->rx_stream)) { - err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) - goto err_locked; - substream->runtime->hw.rate_min = rate; - substream->runtime->hw.rate_max = rate; - } - snd_pcm_set_sync(substream); end: return err; @@ -230,6 +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[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -243,7 +160,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; } @@ -251,6 +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[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -264,7 +182,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; } @@ -304,26 +222,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[substream->pcm->device]; 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[substream->pcm->device]; 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; } @@ -331,13 +251,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[substream->pcm->device]; 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; @@ -348,13 +269,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[substream->pcm->device]; 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; @@ -366,14 +288,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[substream->pcm->device]; - 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[substream->pcm->device]; - return amdtp_stream_pcm_pointer(&dice->rx_stream); + return amdtp_stream_pcm_pointer(stream); } int snd_dice_create_pcm(struct snd_dice *dice) @@ -402,29 +326,53 @@ int snd_dice_create_pcm(struct snd_dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; + __be32 reg; struct snd_pcm *pcm; - unsigned int i, capture, playback; + unsigned int i, max_capture, max_playback, capture, playback; int err; - capture = playback = 0; - for (i = 0; i < 3; i++) { - if (dice->tx_channels[i] > 0) + /* Check whether PCM substreams are required. */ + 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); + } + + for (i = 0; i < MAX_STREAMS; i++) { + capture = playback = 0; + if (i < max_capture) capture = 1; - if (dice->rx_channels[i] > 0) + if (i < max_playback) playback = 1; - } + if (capture == 0 && playback == 0) + break; - err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); - if (err < 0) - return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); + 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 (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); + if (playback > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &playback_ops); + } return 0; } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index a6a39f7ef58d..845d5e5884a4 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -10,6 +10,12 @@ #include "dice.h" #define CALLBACK_TIMEOUT 200 +#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) + +struct reg_params { + unsigned int count; + unsigned int size; +}; const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ @@ -24,96 +30,126 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { [6] = 192000, }; -int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, - unsigned int *mode) +/* + * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE + * to GLOBAL_STATUS. Especially, just after powering on, these are different. + */ +static int ensure_phase_lock(struct snd_dice *dice) { - int i; + __be32 reg, nominal; + int err; + + err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, + ®, sizeof(reg)); + if (err < 0) + return err; - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { - if (!(dice->clock_caps & BIT(i))) - continue; - if (snd_dice_rates[i] != rate) - continue; + if (completion_done(&dice->clock_accepted)) + reinit_completion(&dice->clock_accepted); - *mode = (i - 1) / 2; - return 0; + err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, + ®, sizeof(reg)); + if (err < 0) + return err; + + if (wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { + /* + * Old versions of Dice firmware transfer no notification when + * the same clock status as current one is set. In this case, + * just check current clock status. + */ + err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS, + &nominal, sizeof(nominal)); + if (err < 0) + return err; + if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED)) + return -ETIMEDOUT; } - return -EINVAL; -} -static void release_resources(struct snd_dice *dice, - struct fw_iso_resources *resources) -{ - __be32 channel; - - /* Reset channel number */ - channel = cpu_to_be32((u32)-1); - if (resources == &dice->tx_resources) - 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); + return 0; } -static int keep_resources(struct snd_dice *dice, - struct fw_iso_resources *resources, - unsigned int max_payload_bytes) +static int get_register_params(struct snd_dice *dice, + struct reg_params *tx_params, + struct reg_params *rx_params) { - __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; + tx_params->count = + min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + tx_params->size = be32_to_cpu(reg[1]) * 4; - /* Set channel number */ - channel = cpu_to_be32(resources->channel); - if (resources == &dice->tx_resources) - 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; + rx_params->count = + min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + rx_params->size = 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; - if (stream == &dice->tx_stream) - release_resources(dice, &dice->tx_resources); - else - release_resources(dice, &dice->rx_resources); + 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]); + } + + 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, + struct reg_params *params) { + __be32 reg; + unsigned int i; + + for (i = 0; i < params->count; i++) { + reg = cpu_to_be32((u32)-1); + if (dir == AMDTP_IN_STREAM) { + snd_dice_transaction_write_tx(dice, + params->size * i + TX_ISOCHRONOUS, + ®, sizeof(reg)); + } else { + snd_dice_transaction_write_rx(dice, + params->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; - unsigned int i, mode, pcm_chs, midi_ports; bool double_pcm_frames; + unsigned int i; int err; - err = snd_dice_stream_get_rate_mode(dice, rate, &mode); - if (err < 0) - goto end; - if (stream == &dice->tx_stream) { - resources = &dice->tx_resources; - pcm_chs = dice->tx_channels[mode]; - midi_ports = dice->tx_midi_ports[mode]; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources; - pcm_chs = dice->rx_channels[mode]; - midi_ports = dice->rx_midi_ports[mode]; + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; } /* @@ -126,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, * For this quirk, blocking mode is required and PCM buffer size should * be aligned to SYT_INTERVAL. */ - double_pcm_frames = mode > 1; + double_pcm_frames = rate > 96000; if (double_pcm_frames) { rate /= 2; pcm_chs *= 2; @@ -135,7 +171,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; @@ -147,158 +183,201 @@ 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; - } - - err = amdtp_stream_start(stream, resources->channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - release_resources(dice, resources); -end: - return err; + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), + fw_parent_device(dice->unit)->max_speed); } -static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) +static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int rate, struct reg_params *params) { - u32 source; - int err; + __be32 reg[2]; + unsigned int i, pcm_chs, midi_ports; + struct amdtp_stream *streams; + struct fw_iso_resources *resources; + int err = 0; - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto end; + 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 < params->count; i++) { + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_read_tx(dice, + params->size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); + } else { + err = snd_dice_transaction_read_rx(dice, + params->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, + params->size * i + TX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } else { + err = snd_dice_transaction_write_rx(dice, + params->size * i + RX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } + if (err < 0) + return err; - switch (source) { - /* So-called 'SYT Match' modes, sync_to_syt value of packets received */ - case CLOCK_SOURCE_ARX4: /* in 4th stream */ - case CLOCK_SOURCE_ARX3: /* in 3rd stream */ - case CLOCK_SOURCE_ARX2: /* in 2nd stream */ - err = -ENOSYS; - break; - case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */ - *sync_mode = 0; - break; - default: - *sync_mode = CIP_SYNC_TO_DEVICE; - break; + err = amdtp_stream_start(&streams[i], resources[i].channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; } -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; - enum cip_flags sync_mode; - int err = 0; + unsigned int i; + struct reg_params tx_params, rx_params; + bool need_to_start; + int err; if (dice->substreams_counter == 0) - goto end; + return -EIO; - err = get_sync_mode(dice, &sync_mode); + err = get_register_params(dice, &tx_params, &rx_params); if (err < 0) - goto end; - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master = &dice->tx_stream; - slave = &dice->rx_stream; - } else { - master = &dice->rx_stream; - slave = &dice->tx_stream; - } - - /* Some packet queueing errors. */ - if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) - stop_stream(dice, master); + 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) - stop_stream(dice, master); + return -EINVAL; + + /* Judge to need to restart streams. */ + for (i = 0; i < MAX_STREAMS; i++) { + if (i < tx_params.count) { + if (amdtp_streaming_error(&dice->tx_stream[i]) || + !amdtp_stream_running(&dice->tx_stream[i])) + break; + } + if (i < rx_params.count) { + 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, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + release_resources(dice); - amdtp_stream_set_sync(sync_mode, master, slave); - - err = snd_dice_transaction_set_rate(dice, rate); + err = ensure_phase_lock(dice); if (err < 0) { dev_err(&dice->unit->device, - "fail to set sampling rate\n"); - goto end; + "fail to ensure phase lock\n"); + 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, &tx_params); + if (err < 0) + goto error; + err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); + 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 < tx_params.count && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < rx_params.count && + !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, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + 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) { + struct reg_params tx_params, rx_params; + if (dice->substreams_counter > 0) return; snd_dice_transaction_clear_enable(dice); - stop_stream(dice, &dice->tx_stream); - stop_stream(dice, &dice->rx_stream); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + } + + 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) { - resources = &dice->tx_resources; - dir = AMDTP_IN_STREAM; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources; - dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; } err = fw_iso_resources_init(resources, dice->unit); @@ -319,14 +398,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) - resources = &dice->tx_resources; - else - resources = &dice->rx_resources; + 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); @@ -334,33 +419,51 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) int snd_dice_stream_init_duplex(struct snd_dice *dice) { - int err; - - dice->substreams_counter = 0; + int i, err; - err = init_stream(dice, &dice->tx_stream); - 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); - if (err < 0) - destroy_stream(dice, &dice->tx_stream); + 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) { + struct reg_params tx_params, rx_params; + snd_dice_transaction_clear_enable(dice); - destroy_stream(dice, &dice->tx_stream); - destroy_stream(dice, &dice->rx_stream); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + } + + release_resources(dice); dice->substreams_counter = 0; } void snd_dice_stream_update_duplex(struct snd_dice *dice) { + struct reg_params tx_params, rx_params; + /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -371,11 +474,10 @@ 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); - - fw_iso_resources_update(&dice->rx_resources); - fw_iso_resources_update(&dice->tx_resources); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + } } static void dice_lock_changed(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index a4ff4e0bc0af..0f0350320ae8 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -9,8 +9,6 @@ #include "dice.h" -#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) - static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, u64 offset) { @@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info) info, 4); } -static int set_clock_info(struct snd_dice *dice, - unsigned int rate, unsigned int source) -{ - unsigned int i; - __be32 info; - u32 mask; - u32 clock; - int err; - - err = get_clock_info(dice, &info); - if (err < 0) - return err; - - clock = be32_to_cpu(info); - if (source != UINT_MAX) { - mask = CLOCK_SOURCE_MASK; - clock &= ~mask; - clock |= source; - } - if (rate != UINT_MAX) { - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { - if (snd_dice_rates[i] == rate) - break; - } - if (i == ARRAY_SIZE(snd_dice_rates)) - return -EINVAL; - - mask = CLOCK_RATE_MASK; - clock &= ~mask; - clock |= i << CLOCK_RATE_SHIFT; - } - info = cpu_to_be32(clock); - - if (completion_done(&dice->clock_accepted)) - reinit_completion(&dice->clock_accepted); - - err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, - &info, 4); - if (err < 0) - return err; - - if (wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) - return -ETIMEDOUT; - - return 0; -} - int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source) { @@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) end: return err; } -int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) -{ - return set_clock_info(dice, rate, UINT_MAX); -} int snd_dice_transaction_set_enable(struct snd_dice *dice) { @@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_COMPLETE); - if (bits & NOTIFY_CLOCK_ACCEPTED) + if (bits & NOTIFY_LOCK_CHG) complete(&dice->clock_accepted); wake_up(&dice->hwdep_wait); } diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index b91b3739c810..8b64aef31a86 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) @@ -57,65 +95,10 @@ static int check_dice_category(struct fw_unit *unit) return 0; } -static int highest_supported_mode_rate(struct snd_dice *dice, - unsigned int mode, unsigned int *rate) -{ - unsigned int i, m; - - for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) { - *rate = snd_dice_rates[i - 1]; - if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0) - continue; - if (mode == m) - break; - } - if (i == 0) - return -EINVAL; - - return 0; -} - -static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) -{ - __be32 values[2]; - unsigned int rate; - int err; - - if (highest_supported_mode_rate(dice, mode, &rate) < 0) { - dice->tx_channels[mode] = 0; - dice->tx_midi_ports[mode] = 0; - dice->rx_channels[mode] = 0; - dice->rx_midi_ports[mode] = 0; - return 0; - } - - err = snd_dice_transaction_set_rate(dice, rate); - if (err < 0) - return err; - - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - values, sizeof(values)); - if (err < 0) - return err; - - dice->tx_channels[mode] = be32_to_cpu(values[0]); - dice->tx_midi_ports[mode] = be32_to_cpu(values[1]); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - values, sizeof(values)); - if (err < 0) - return err; - - dice->rx_channels[mode] = be32_to_cpu(values[0]); - dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); - - return 0; -} - -static int dice_read_params(struct snd_dice *dice) +static int check_clock_caps(struct snd_dice *dice) { __be32 value; - int mode, err; + int err; /* some very old firmwares don't tell about their clock support */ if (dice->clock_caps > 0) { @@ -133,12 +116,6 @@ static int dice_read_params(struct snd_dice *dice) CLOCK_CAP_SOURCE_INTERNAL; } - for (mode = 2; mode >= 0; --mode) { - err = dice_read_mode_params(dice, mode); - if (err < 0) - return err; - } - return 0; } @@ -211,11 +188,14 @@ 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; - err = dice_read_params(dice); + err = check_clock_caps(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 3d5ebebe61ea..e6c07857f475 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; @@ -56,10 +79,6 @@ struct snd_dice { unsigned int rsrv_offset; unsigned int clock_caps; - unsigned int tx_channels[3]; - unsigned int rx_channels[3]; - unsigned int tx_midi_ports[3]; - unsigned int rx_midi_ports[3]; struct fw_address_handler notification_handler; int owner_generation; @@ -71,13 +90,15 @@ 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; + + bool force_two_pcms; }; enum snd_dice_addr_type { @@ -158,7 +179,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source); -int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate); int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); int snd_dice_transaction_set_enable(struct snd_dice *dice); void snd_dice_transaction_clear_enable(struct snd_dice *dice); @@ -169,9 +189,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice); #define SND_DICE_RATES_COUNT 7 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; -int snd_dice_stream_get_rate_mode(struct snd_dice *dice, - unsigned int rate, unsigned int *mode); - int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); void snd_dice_stream_stop_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice); diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index b02a5e8cad44..0ac92aba5bc1 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -63,7 +63,7 @@ struct amdtp_dot { #define BYTE_PER_SAMPLE (4) #define MAGIC_DOT_BYTE (2) #define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE) -static const u8 dot_scrt(const u8 idx, const unsigned int off) +static u8 dot_scrt(const u8 idx, const unsigned int off) { /* * the length of the added pattern only depends on the lower nibble diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index d5b19bc11e59..8f27b67503c8 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit) struct snd_efw *efw = dev_get_drvdata(&unit->device); snd_efw_transaction_bus_reset(efw->unit); + + mutex_lock(&efw->mutex); snd_efw_stream_update_duplex(efw); + mutex_unlock(&efw->mutex); } static void efw_remove(struct fw_unit *unit) diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 968a40a1beb2..425db8d88235 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw) void snd_efw_stream_update_duplex(struct snd_efw *efw) { - if ((cmp_connection_update(&efw->out_conn) < 0) || - (cmp_connection_update(&efw->in_conn) < 0)) { - mutex_lock(&efw->mutex); + if (cmp_connection_update(&efw->out_conn) < 0 || + cmp_connection_update(&efw->in_conn) < 0) { stop_stream(efw, &efw->rx_stream); stop_stream(efw, &efw->tx_stream); - mutex_unlock(&efw->mutex); } else { amdtp_stream_update(&efw->rx_stream); amdtp_stream_update(&efw->tx_stream); diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index bb53eb35721b..f897c9831077 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -26,11 +26,13 @@ struct fw_scs1x { u8 output_bytes; bool output_escaped; bool output_escape_high_nibble; - struct tasklet_struct tasklet; + struct work_struct work; wait_queue_head_t idle_wait; u8 buffer[HSS1394_MAX_PACKET_SIZE]; bool transaction_running; struct fw_transaction transaction; + unsigned int transaction_bytes; + bool error; struct fw_device *fw_dev; }; @@ -125,11 +127,16 @@ static void scs_write_callback(struct fw_card *card, int rcode, { struct fw_scs1x *scs = callback_data; - if (rcode == RCODE_GENERATION) - ; /* TODO: retry this packet */ + if (!rcode_is_permanent_error(rcode)) { + /* Don't retry for this data. */ + if (rcode == RCODE_COMPLETE) + scs->transaction_bytes = 0; + } else { + scs->error = true; + } scs->transaction_running = false; - tasklet_schedule(&scs->tasklet); + schedule_work(&scs->work); } static bool is_valid_running_status(u8 status) @@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status) status == 0xfd; } -static void scs_output_tasklet(unsigned long data) +static void scs_output_work(struct work_struct *work) { - struct fw_scs1x *scs = (struct fw_scs1x *)data; + struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work); struct snd_rawmidi_substream *stream; unsigned int i; u8 byte; @@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data) return; stream = ACCESS_ONCE(scs->output); - if (!stream) { + if (!stream || scs->error) { scs->output_idle = true; wake_up(&scs->idle_wait); return; } + if (scs->transaction_bytes > 0) + goto retry; + i = scs->output_bytes; for (;;) { if (snd_rawmidi_transmit(stream, &byte, 1) != 1) { @@ -253,13 +263,16 @@ static void scs_output_tasklet(unsigned long data) scs->output_bytes = 1; scs->output_escaped = false; + scs->transaction_bytes = i; +retry: scs->transaction_running = true; generation = scs->fw_dev->generation; smp_rmb(); /* node_id vs. generation */ fw_send_request(scs->fw_dev->card, &scs->transaction, TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id, generation, scs->fw_dev->max_speed, HSS1394_ADDRESS, - scs->buffer, i, scs_write_callback, scs); + scs->buffer, scs->transaction_bytes, + scs_write_callback, scs); } static int midi_capture_open(struct snd_rawmidi_substream *stream) @@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) scs->output_bytes = 1; scs->output_escaped = false; scs->output_idle = false; + scs->transaction_bytes = 0; + scs->error = false; ACCESS_ONCE(scs->output) = stream; - tasklet_schedule(&scs->tasklet); + schedule_work(&scs->work); } else { ACCESS_ONCE(scs->output) = NULL; } @@ -395,7 +410,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &midi_playback_ops); - tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs); + INIT_WORK(&scs->work, scs_output_work); init_waitqueue_head(&scs->idle_wait); scs->output_idle = true; diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 904ce0329fa1..040a96d1ba8e 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -230,6 +230,7 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm) return err; error: fw_core_remove_address_handler(&tscm->async_handler); + tscm->async_handler.callback_data = NULL; return err; } @@ -276,6 +277,9 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) __be32 reg; unsigned int i; + if (tscm->async_handler.callback_data == NULL) + return; + /* Turn off FireWire LED. */ reg = cpu_to_be32(0x0000008e); snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, @@ -297,6 +301,8 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) ®, sizeof(reg), 0); fw_core_remove_address_handler(&tscm->async_handler); + tscm->async_handler.callback_data = NULL; + for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) snd_fw_async_midi_port_destroy(&tscm->out_ports[i]); } diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index ee0bc1839508..e281c338e562 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -21,7 +21,6 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 8, .midi_capture_ports = 4, .midi_playback_ports = 4, - .is_controller = true, }, { .name = "FW-1082", @@ -31,9 +30,16 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 2, .midi_capture_ports = 2, .midi_playback_ports = 2, - .is_controller = true, }, - /* FW-1804 may be supported. */ + { + .name = "FW-1804", + .has_adat = true, + .has_spdif = true, + .pcm_capture_analog_channels = 8, + .pcm_playback_analog_channels = 2, + .midi_capture_ports = 2, + .midi_playback_ports = 4, + }, }; static int identify_model(struct snd_tscm *tscm) diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 2d028d2bd3bd..30ab77e924f7 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -39,7 +39,6 @@ struct snd_tscm_spec { unsigned int pcm_playback_analog_channels; unsigned int midi_capture_ports; unsigned int midi_playback_ports; - bool is_controller; }; #define TSCM_MIDI_IN_PORT_MAX 4 @@ -72,9 +71,6 @@ struct snd_tscm { struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX]; u8 running_status[TSCM_MIDI_OUT_PORT_MAX]; bool on_sysex[TSCM_MIDI_OUT_PORT_MAX]; - - /* For control messages. */ - struct snd_firewire_tascam_status *status; }; #define TSCM_ADDR_BASE 0xffff00000000ull |