// SPDX-License-Identifier: GPL-2.0-only /* * dice_stream.c - a part of driver for DICE based devices * * Copyright (c) Clemens Ladisch <clemens@ladisch.de> * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*/
data &= ~CLOCK_RATE_MASK; 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;
data |= i << CLOCK_RATE_SHIFT;
if (completion_done(&dice->clock_accepted))
reinit_completion(&dice->clock_accepted);
new = cpu_to_be32(data);
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
&new, sizeof(new)); if (err < 0) return err;
if (wait_for_completion_timeout(&dice->clock_accepted,
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { if (reg != new) return -ETIMEDOUT;
}
// 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 // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are // transferred on AMDTP packets at 96 kHz. Two successive samples of a // channel are stored consecutively in the packet. This quirk is called // as 'Dual Wire'. // For this quirk, blocking mode is required and PCM buffer size should // be aligned to SYT_INTERVAL.
double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames); if (double_pcm_frames) {
rate /= 2;
pcm_chs *= 2;
}
// These are important for developer of this driver. if (pcm_chs != pcm_cache) {
dev_info(&dice->unit->device, "cache mismatch: pcm: %u:%u, midi: %u\n",
pcm_chs, pcm_cache, midi_ports); return -EPROTO;
}
// Just after owning the unit (GLOBAL_OWNER), the unit can // return invalid stream formats. Selecting clock parameters // have an effect for the unit to refine it.
err = select_clock(dice, rate); if (err < 0) return err;
// After changing sampling transfer frequency, the value of // register can be changed.
err = get_register_params(dice, &tx_params, &rx_params); if (err < 0) 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)
{ unsignedint generation = dice->rx_resources[0].generation; struct reg_params tx_params, rx_params; unsignedint i; unsignedint rate; enum snd_dice_rate_mode mode; int err;
// Check error of packet streaming. for (i = 0; i < MAX_STREAMS; ++i) { if (amdtp_streaming_error(&dice->tx_stream[i]) ||
amdtp_streaming_error(&dice->rx_stream[i])) {
amdtp_domain_stop(&dice->domain);
finish_session(dice, &tx_params, &rx_params); break;
}
}
if (generation != fw_parent_device(dice->unit)->card->generation) { for (i = 0; i < MAX_STREAMS; ++i) { if (i < tx_params.count)
fw_iso_resources_update(dice->tx_resources + i); if (i < rx_params.count)
fw_iso_resources_update(dice->rx_resources + i);
}
}
// Check required streams are running or not.
err = snd_dice_transaction_get_rate(dice, &rate); if (err < 0) return err;
err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (err < 0) return err; for (i = 0; i < MAX_STREAMS; ++i) { if (dice->tx_pcm_chs[i][mode] > 0 &&
!amdtp_stream_running(&dice->tx_stream[i])) break; if (dice->rx_pcm_chs[i][mode] > 0 &&
!amdtp_stream_running(&dice->rx_stream[i])) break;
} if (i < MAX_STREAMS) { // Start both streams.
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_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"); goto error;
}
// MEMO: The device immediately starts packet transmission when enabled. Some // devices are strictly to generate any discontinuity in the sequence of tx packet // when they receives invalid sequence of presentation time in CIP header. The // sequence replay for media clock recovery can suppress the behaviour.
err = amdtp_domain_start(&dice->domain, 0, true, false); if (err < 0) goto error;
/* * 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) { if (get_register_params(dice, &tx_params, &rx_params) >= 0)
finish_session(dice, &tx_params, &rx_params);
/* * This function should be called before starting streams or after stopping * streams.
*/ staticvoid destroy_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, unsignedint index)
{ struct amdtp_stream *stream; struct fw_iso_resources *resources;
int snd_dice_stream_init_duplex(struct snd_dice *dice)
{ int i, err;
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_IN_STREAM, i); goto end;
}
}
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); goto end;
}
}
err = amdtp_domain_init(&dice->domain); if (err < 0) { for (i = 0; i < MAX_STREAMS; ++i) {
destroy_stream(dice, AMDTP_OUT_STREAM, i);
destroy_stream(dice, AMDTP_IN_STREAM, i);
}
}
end: return err;
}
/* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before * it can react to any of our attempts to reenable streaming. This * means that we lose synchronization anyway, so we force our streams * to stop so that the application can restart them in an orderly * manner.
*/
dice->global_enabled = false;
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
amdtp_domain_stop(&dice->domain);
int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
{ unsignedint rate; enum snd_dice_rate_mode mode;
__be32 reg[2]; struct reg_params tx_params, rx_params; int i; int err;
/* If extended protocol is available, detect detail spec. */
err = snd_dice_detect_extension_formats(dice); if (err >= 0) return err;
/* * Available stream format is restricted at current mode of sampling * clock.
*/
err = snd_dice_transaction_get_rate(dice, &rate); if (err < 0) return err;
/* * Just after owning the unit (GLOBAL_OWNER), the unit can return * invalid stream formats. Selecting clock parameters have an effect * for the unit to refine it.
*/
err = select_clock(dice, rate); if (err < 0) return err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.