summaryrefslogtreecommitdiff
path: root/sound/firewire/fireface/ff-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/fireface/ff-stream.c')
-rw-r--r--sound/firewire/fireface/ff-stream.c244
1 files changed, 110 insertions, 134 deletions
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
index a490e4553721..ba42490f2b0e 100644
--- a/sound/firewire/fireface/ff-stream.c
+++ b/sound/firewire/fireface/ff-stream.c
@@ -1,14 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ff-stream.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
-#define CALLBACK_TIMEOUT_MS 200
+#define READY_TIMEOUT_MS 200
int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
enum snd_ff_stream_mode *mode)
@@ -31,100 +30,67 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
return 0;
}
-static void release_resources(struct snd_ff *ff)
-{
- fw_iso_resources_free(&ff->tx_resources);
- fw_iso_resources_free(&ff->rx_resources);
-}
-
-static int switch_fetching_mode(struct snd_ff *ff, bool enable)
-{
- unsigned int count;
- __le32 *reg;
- int i;
- int err;
-
- count = 0;
- for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
- count = max(count, ff->spec->pcm_playback_channels[i]);
-
- reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
- if (!reg)
- return -ENOMEM;
-
- if (!enable) {
- /*
- * Each quadlet is corresponding to data channels in a data
- * blocks in reverse order. Precisely, quadlets for available
- * data channels should be enabled. Here, I take second best
- * to fetch PCM frames from all of data channels regardless of
- * stf.
- */
- for (i = 0; i < count; ++i)
- reg[i] = cpu_to_le32(0x00000001);
- }
-
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
- SND_FF_REG_FETCH_PCM_FRAMES, reg,
- sizeof(__le32) * count, 0);
- kfree(reg);
- return err;
-}
-
static inline void finish_session(struct snd_ff *ff)
{
ff->spec->protocol->finish_session(ff);
- switch_fetching_mode(ff, false);
+ ff->spec->protocol->switch_fetching_mode(ff, false);
}
-static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
+static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
{
- int err;
struct fw_iso_resources *resources;
- struct amdtp_stream *stream;
+ enum amdtp_stream_direction dir;
+ int err;
- if (dir == AMDTP_IN_STREAM) {
+ if (s == &ff->tx_stream) {
resources = &ff->tx_resources;
- stream = &ff->tx_stream;
+ dir = AMDTP_IN_STREAM;
} else {
resources = &ff->rx_resources;
- stream = &ff->rx_stream;
+ dir = AMDTP_OUT_STREAM;
}
err = fw_iso_resources_init(resources, ff->unit);
if (err < 0)
return err;
- err = amdtp_ff_init(stream, ff->unit, dir);
+ err = amdtp_ff_init(s, ff->unit, dir);
if (err < 0)
fw_iso_resources_destroy(resources);
return err;
}
-static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
+static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
{
- if (dir == AMDTP_IN_STREAM) {
- amdtp_stream_destroy(&ff->tx_stream);
+ amdtp_stream_destroy(s);
+
+ if (s == &ff->tx_stream)
fw_iso_resources_destroy(&ff->tx_resources);
- } else {
- amdtp_stream_destroy(&ff->rx_stream);
+ else
fw_iso_resources_destroy(&ff->rx_resources);
- }
}
int snd_ff_stream_init_duplex(struct snd_ff *ff)
{
int err;
- err = init_stream(ff, AMDTP_OUT_STREAM);
+ err = init_stream(ff, &ff->rx_stream);
if (err < 0)
- goto end;
+ return err;
+
+ err = init_stream(ff, &ff->tx_stream);
+ if (err < 0) {
+ destroy_stream(ff, &ff->rx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&ff->domain);
+ if (err < 0) {
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
+ }
- err = init_stream(ff, AMDTP_IN_STREAM);
- if (err < 0)
- destroy_stream(ff, AMDTP_OUT_STREAM);
-end:
return err;
}
@@ -134,41 +100,34 @@ end:
*/
void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
{
- destroy_stream(ff, AMDTP_IN_STREAM);
- destroy_stream(ff, AMDTP_OUT_STREAM);
+ amdtp_domain_destroy(&ff->domain);
+
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
}
-int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
{
unsigned int curr_rate;
enum snd_ff_clock_src src;
int err;
- if (ff->substreams_counter == 0)
- return 0;
-
- err = snd_ff_transaction_get_clock(ff, &curr_rate, &src);
+ err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
if (err < 0)
return err;
- if (curr_rate != rate ||
- amdtp_streaming_error(&ff->tx_stream) ||
- amdtp_streaming_error(&ff->rx_stream)) {
- finish_session(ff);
-
- amdtp_stream_stop(&ff->tx_stream);
- amdtp_stream_stop(&ff->rx_stream);
- release_resources(ff);
- }
-
- /*
- * Regardless of current source of clock signal, drivers transfer some
- * packets. Then, the device transfers packets.
- */
- if (!amdtp_stream_running(&ff->rx_stream)) {
+ if (ff->substreams_counter == 0 || curr_rate != rate) {
enum snd_ff_stream_mode mode;
int i;
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+
for (i = 0; i < CIP_SFC_COUNT; ++i) {
if (amdtp_rate_table[i] == rate)
break;
@@ -190,74 +149,100 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
if (err < 0)
return err;
- err = ff->spec->protocol->begin_session(ff, rate);
+ err = ff->spec->protocol->allocate_resources(ff, rate);
if (err < 0)
- goto error;
+ return err;
- err = amdtp_stream_start(&ff->rx_stream,
- ff->rx_resources.channel,
- fw_parent_device(ff->unit)->max_speed);
+ err = amdtp_domain_set_events_per_period(&ff->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+{
+ int err;
+
+ if (ff->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&ff->tx_stream) ||
+ amdtp_streaming_error(&ff->rx_stream)) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+ }
+
+ /*
+ * Regardless of current source of clock signal, drivers transfer some
+ * packets. Then, the device transfers packets.
+ */
+ if (!amdtp_stream_running(&ff->rx_stream)) {
+ int spd = fw_parent_device(ff->unit)->max_speed;
+
+ err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&ff->rx_stream,
- CALLBACK_TIMEOUT_MS)) {
- err = -ETIMEDOUT;
+ err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
+ ff->rx_resources.channel, spd);
+ if (err < 0)
goto error;
- }
- err = switch_fetching_mode(ff, true);
+ err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
+ ff->tx_resources.channel, spd);
if (err < 0)
goto error;
- }
- if (!amdtp_stream_running(&ff->tx_stream)) {
- err = amdtp_stream_start(&ff->tx_stream,
- ff->tx_resources.channel,
- fw_parent_device(ff->unit)->max_speed);
+ // NOTE: The device doesn't transfer packets unless receiving any packet. The
+ // sequence of tx packets includes cycle skip corresponding to empty packet or
+ // NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&ff->domain, 0, true, true);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&ff->tx_stream,
- CALLBACK_TIMEOUT_MS)) {
+ if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
+
+ err = ff->spec->protocol->switch_fetching_mode(ff, true);
+ if (err < 0)
+ goto error;
}
return 0;
error:
- amdtp_stream_stop(&ff->tx_stream);
- amdtp_stream_stop(&ff->rx_stream);
-
+ amdtp_domain_stop(&ff->domain);
finish_session(ff);
- release_resources(ff);
return err;
}
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
{
- if (ff->substreams_counter > 0)
- return;
+ if (ff->substreams_counter == 0) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
- amdtp_stream_stop(&ff->tx_stream);
- amdtp_stream_stop(&ff->rx_stream);
- finish_session(ff);
- release_resources(ff);
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+ }
}
void snd_ff_stream_update_duplex(struct snd_ff *ff)
{
- /* The device discontinue to transfer packets. */
- amdtp_stream_pcm_abort(&ff->tx_stream);
- amdtp_stream_stop(&ff->tx_stream);
+ amdtp_domain_stop(&ff->domain);
+ // The device discontinue to transfer packets.
+ amdtp_stream_pcm_abort(&ff->tx_stream);
amdtp_stream_pcm_abort(&ff->rx_stream);
- amdtp_stream_stop(&ff->rx_stream);
-
- fw_iso_resources_update(&ff->tx_resources);
- fw_iso_resources_update(&ff->rx_resources);
}
void snd_ff_stream_lock_changed(struct snd_ff *ff)
@@ -268,33 +253,24 @@ void snd_ff_stream_lock_changed(struct snd_ff *ff)
int snd_ff_stream_lock_try(struct snd_ff *ff)
{
- int err;
-
- spin_lock_irq(&ff->lock);
+ guard(spinlock_irq)(&ff->lock);
/* user land lock this */
- if (ff->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
+ if (ff->dev_lock_count < 0)
+ return -EBUSY;
/* this is the first time */
if (ff->dev_lock_count++ == 0)
snd_ff_stream_lock_changed(ff);
- err = 0;
-end:
- spin_unlock_irq(&ff->lock);
- return err;
+ return 0;
}
void snd_ff_stream_lock_release(struct snd_ff *ff)
{
- spin_lock_irq(&ff->lock);
+ guard(spinlock_irq)(&ff->lock);
if (WARN_ON(ff->dev_lock_count <= 0))
- goto end;
+ return;
if (--ff->dev_lock_count == 0)
snd_ff_stream_lock_changed(ff);
-end:
- spin_unlock_irq(&ff->lock);
}