forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ALSA: fireface: add stream management functionality
This commit adds management functionality for packet streaming. As long as investigating Fireface 400, there're three modes depending on sampling transmission frequency. The number of data channels in each data block is different depending on the mode. The set of available data channels for each mode might be different for each protocol and model. The length of registers for the number of isochronous channel is just three bits, therefore 0-7ch are available. When bus reset occurs on IEEE 1394 bus, the device discontinues to transmit packets. This commit aborts PCM substreams at bus reset handler. As I described in followed commits, The device manages its sampling clock independently of sampling transmission frequency against IEC 61883-6. Thus, it's a lower cost to change the sampling transmission frequency, while data fetch between streaming layer and DSP require larger buffer for resampling. As a result, device latency might tend to be larger than ASICs for IEC 61883-1/6 such as DM1000/DM1100/DM1500 (BeBoB), DiceII/TCD2210/TCD2220/TCD3070 and OXFW970/971. Signed-off-by: Takashi Sakamoto <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
- Loading branch information
Showing
4 changed files
with
267 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o | ||
snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ | ||
ff-stream.o | ||
obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
/* | ||
* 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 | ||
|
||
static int get_rate_mode(unsigned int rate, unsigned int *mode) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < CIP_SFC_COUNT; i++) { | ||
if (amdtp_rate_table[i] == rate) | ||
break; | ||
} | ||
|
||
if (i == CIP_SFC_COUNT) | ||
return -EINVAL; | ||
|
||
*mode = ((int)i - 1) / 2; | ||
|
||
return 0; | ||
} | ||
|
||
/* | ||
* Fireface 400 manages isochronous channel number in 3 bit field. Therefore, | ||
* we can allocate between 0 and 7 channel. | ||
*/ | ||
static int keep_resources(struct snd_ff *ff, unsigned int rate) | ||
{ | ||
int mode; | ||
int err; | ||
|
||
err = get_rate_mode(rate, &mode); | ||
if (err < 0) | ||
return err; | ||
|
||
/* Keep resources for in-stream. */ | ||
err = amdtp_ff_set_parameters(&ff->tx_stream, rate, | ||
ff->spec->pcm_capture_channels[mode]); | ||
if (err < 0) | ||
return err; | ||
ff->tx_resources.channels_mask = 0x00000000000000ffuLL; | ||
err = fw_iso_resources_allocate(&ff->tx_resources, | ||
amdtp_stream_get_max_payload(&ff->tx_stream), | ||
fw_parent_device(ff->unit)->max_speed); | ||
if (err < 0) | ||
return err; | ||
|
||
/* Keep resources for out-stream. */ | ||
err = amdtp_ff_set_parameters(&ff->rx_stream, rate, | ||
ff->spec->pcm_playback_channels[mode]); | ||
if (err < 0) | ||
return err; | ||
ff->rx_resources.channels_mask = 0x00000000000000ffuLL; | ||
err = fw_iso_resources_allocate(&ff->rx_resources, | ||
amdtp_stream_get_max_payload(&ff->rx_stream), | ||
fw_parent_device(ff->unit)->max_speed); | ||
if (err < 0) | ||
fw_iso_resources_free(&ff->tx_resources); | ||
|
||
return err; | ||
} | ||
|
||
static void release_resources(struct snd_ff *ff) | ||
{ | ||
fw_iso_resources_free(&ff->tx_resources); | ||
fw_iso_resources_free(&ff->rx_resources); | ||
} | ||
|
||
static inline void finish_session(struct snd_ff *ff) | ||
{ | ||
ff->spec->protocol->finish_session(ff); | ||
ff->spec->protocol->switch_fetching_mode(ff, false); | ||
} | ||
|
||
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) | ||
{ | ||
int err; | ||
struct fw_iso_resources *resources; | ||
struct amdtp_stream *stream; | ||
|
||
if (dir == AMDTP_IN_STREAM) { | ||
resources = &ff->tx_resources; | ||
stream = &ff->tx_stream; | ||
} else { | ||
resources = &ff->rx_resources; | ||
stream = &ff->rx_stream; | ||
} | ||
|
||
err = fw_iso_resources_init(resources, ff->unit); | ||
if (err < 0) | ||
return err; | ||
|
||
err = amdtp_ff_init(stream, 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) | ||
{ | ||
if (dir == AMDTP_IN_STREAM) { | ||
amdtp_stream_destroy(&ff->tx_stream); | ||
fw_iso_resources_destroy(&ff->tx_resources); | ||
} else { | ||
amdtp_stream_destroy(&ff->rx_stream); | ||
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); | ||
if (err < 0) | ||
goto end; | ||
|
||
err = init_stream(ff, AMDTP_IN_STREAM); | ||
if (err < 0) | ||
destroy_stream(ff, AMDTP_OUT_STREAM); | ||
end: | ||
return err; | ||
} | ||
|
||
/* | ||
* This function should be called before starting streams or after stopping | ||
* streams. | ||
*/ | ||
void snd_ff_stream_destroy_duplex(struct snd_ff *ff) | ||
{ | ||
destroy_stream(ff, AMDTP_IN_STREAM); | ||
destroy_stream(ff, AMDTP_OUT_STREAM); | ||
} | ||
|
||
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) | ||
{ | ||
unsigned int curr_rate; | ||
enum snd_ff_clock_src src; | ||
int err; | ||
|
||
if (ff->substreams_counter == 0) | ||
return 0; | ||
|
||
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)) { | ||
err = keep_resources(ff, rate); | ||
if (err < 0) | ||
goto error; | ||
|
||
err = ff->spec->protocol->begin_session(ff, rate); | ||
if (err < 0) | ||
goto error; | ||
|
||
err = amdtp_stream_start(&ff->rx_stream, | ||
ff->rx_resources.channel, | ||
fw_parent_device(ff->unit)->max_speed); | ||
if (err < 0) | ||
goto error; | ||
|
||
if (!amdtp_stream_wait_callback(&ff->rx_stream, | ||
CALLBACK_TIMEOUT_MS)) { | ||
err = -ETIMEDOUT; | ||
goto error; | ||
} | ||
|
||
err = ff->spec->protocol->switch_fetching_mode(ff, true); | ||
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); | ||
if (err < 0) | ||
goto error; | ||
|
||
if (!amdtp_stream_wait_callback(&ff->tx_stream, | ||
CALLBACK_TIMEOUT_MS)) { | ||
err = -ETIMEDOUT; | ||
goto error; | ||
} | ||
} | ||
|
||
return 0; | ||
error: | ||
amdtp_stream_stop(&ff->tx_stream); | ||
amdtp_stream_stop(&ff->rx_stream); | ||
|
||
finish_session(ff); | ||
release_resources(ff); | ||
|
||
return err; | ||
} | ||
|
||
void snd_ff_stream_stop_duplex(struct snd_ff *ff) | ||
{ | ||
if (ff->substreams_counter > 0) | ||
return; | ||
|
||
amdtp_stream_stop(&ff->tx_stream); | ||
amdtp_stream_stop(&ff->rx_stream); | ||
finish_session(ff); | ||
release_resources(ff); | ||
} | ||
|
||
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_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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters