summaryrefslogtreecommitdiff
path: root/sound/firewire/oxfw/oxfw-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/oxfw/oxfw-stream.c')
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c270
1 files changed, 209 insertions, 61 deletions
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 1820497..1d15428 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -39,6 +39,22 @@ static const unsigned int avc_stream_rate_table[] = {
[5] = 0x07,
};
+static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (oxfw->has_output)
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+end:
+ return err;
+}
+
static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
unsigned int rate, unsigned int pcm_channels)
{
@@ -47,8 +63,13 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
enum avc_general_plug_dir dir;
unsigned int i, err, len;
- formats = oxfw->rx_stream_formats;
- dir = AVC_GENERAL_PLUG_DIR_IN;
+ if (s == &oxfw->tx_stream) {
+ formats = oxfw->tx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ } else {
+ formats = oxfw->rx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ }
/* Seek stream format for requirements. */
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
@@ -64,8 +85,7 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
/* If assumed, just change rate. */
if (oxfw->assumed)
- return avc_general_set_sig_fmt(oxfw->unit, rate,
- AVC_GENERAL_PLUG_DIR_IN, 0);
+ return set_rate(oxfw, rate);
/* Calculate format length. */
len = 5 + formats[i][4] * 2;
@@ -80,47 +100,35 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
return 0;
}
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw)
+static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
- int err;
-
- err = cmp_connection_init(&oxfw->in_conn, oxfw->unit,
- CMP_INPUT, 0);
- if (err < 0)
- goto end;
+ amdtp_stream_pcm_abort(stream);
+ amdtp_stream_stop(stream);
- err = amdtp_stream_init(&oxfw->rx_stream, oxfw->unit,
- AMDTP_OUT_STREAM, CIP_NONBLOCKING);
- if (err < 0) {
- amdtp_stream_destroy(&oxfw->rx_stream);
- cmp_connection_destroy(&oxfw->in_conn);
- }
-end:
- return err;
-}
-
-static void stop_stream(struct snd_oxfw *oxfw)
-{
- amdtp_stream_pcm_abort(&oxfw->rx_stream);
- amdtp_stream_stop(&oxfw->rx_stream);
- cmp_connection_break(&oxfw->in_conn);
+ if (stream == &oxfw->tx_stream)
+ cmp_connection_break(&oxfw->out_conn);
+ else
+ cmp_connection_break(&oxfw->in_conn);
}
-static int start_stream(struct snd_oxfw *oxfw, unsigned int rate,
- unsigned int pcm_channels)
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels)
{
u8 **formats;
struct cmp_connection *conn;
struct snd_oxfw_stream_formation formation;
unsigned int i, midi_ports;
- struct amdtp_stream *stream;
int err;
- stream = &oxfw->rx_stream;
- formats = oxfw->rx_stream_formats;
- conn = &oxfw->in_conn;
+ if (stream == &oxfw->rx_stream) {
+ formats = oxfw->rx_stream_formats;
+ conn = &oxfw->in_conn;
+ } else {
+ formats = oxfw->tx_stream_formats;
+ conn = &oxfw->out_conn;
+ }
- /* Get stream formation */
+ /* Get stream format */
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
if (formats[i] == NULL)
break;
@@ -164,67 +172,196 @@ static int start_stream(struct snd_oxfw *oxfw, unsigned int rate,
/* Wait first packet */
err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT);
if (err < 0)
- stop_stream(oxfw);
+ stop_stream(oxfw, stream);
end:
return err;
}
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, unsigned int rate,
- unsigned int pcm_channels)
+static int check_connection_used_by_others(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(stream)) {
+ dev_err(&oxfw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ int err;
+
+ if (stream == &oxfw->tx_stream) {
+ conn = &oxfw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+ } else {
+ conn = &oxfw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+ goto end;
+ }
+
+ /* OXFW starts to transmit packets with non-zero dbc. */
+ if (stream == &oxfw->tx_stream)
+ oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+end:
+ return err;
+}
+
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ struct amdtp_stream *opposite;
struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ unsigned int substreams, opposite_substreams;
int err = 0;
+ if (stream == &oxfw->tx_stream) {
+ substreams = oxfw->capture_substreams;
+ opposite = &oxfw->rx_stream;
+ opposite_substreams = oxfw->playback_substreams;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ } else {
+ substreams = oxfw->playback_substreams;
+ opposite_substreams = oxfw->capture_substreams;
+
+ if (oxfw->has_output)
+ opposite = &oxfw->rx_stream;
+ else
+ opposite = NULL;
+
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ }
+
+ if (substreams == 0)
+ goto end;
+
+ /*
+ * Considering JACK/FFADO streaming:
+ * TODO: This can be removed hwdep functionality becomes popular.
+ */
+ err = check_connection_used_by_others(oxfw, stream);
+ if (err < 0)
+ goto end;
+
/* packet queueing error */
- if (amdtp_streaming_error(&oxfw->rx_stream))
- stop_stream(oxfw);
+ if (amdtp_streaming_error(stream))
+ stop_stream(oxfw, stream);
- err = snd_oxfw_stream_get_current_formation(oxfw,
- AVC_GENERAL_PLUG_DIR_IN,
- &formation);
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
if (err < 0)
goto end;
if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
- stop_stream(oxfw);
+ if (opposite != NULL) {
+ err = check_connection_used_by_others(oxfw, opposite);
+ if (err < 0)
+ goto end;
+ stop_stream(oxfw, opposite);
+ }
+ stop_stream(oxfw, stream);
- /* arrange sampling rate */
- err = set_stream_format(oxfw, &oxfw->rx_stream, rate,
- pcm_channels);
+ err = set_stream_format(oxfw, stream, rate, pcm_channels);
if (err < 0) {
dev_err(&oxfw->unit->device,
"fail to set stream format: %d\n", err);
goto end;
}
+
+ /* Start opposite stream if needed. */
+ if (opposite && !amdtp_stream_running(opposite) &&
+ (opposite_substreams > 0)) {
+ err = start_stream(oxfw, opposite, rate, 0);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to restart stream: %d\n", err);
+ goto end;
+ }
+ }
}
- err = start_stream(oxfw, rate, pcm_channels);
- if (err < 0)
- dev_err(&oxfw->unit->device,
- "fail to start stream: %d\n", err);
+ /* Start requested stream. */
+ if (!amdtp_stream_running(stream)) {
+ err = start_stream(oxfw, stream, rate, pcm_channels);
+ if (err < 0)
+ dev_err(&oxfw->unit->device,
+ "fail to start stream: %d\n", err);
+ }
end:
return err;
}
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw)
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
{
- stop_stream(oxfw);
+ if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
+ ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
+ return;
+
+ stop_stream(oxfw, stream);
}
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw)
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
{
- stop_stream(oxfw);
+ struct cmp_connection *conn;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
- amdtp_stream_destroy(&oxfw->rx_stream);
- cmp_connection_destroy(&oxfw->in_conn);
+ stop_stream(oxfw, stream);
+
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
}
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw)
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
{
- if (cmp_connection_update(&oxfw->in_conn) < 0)
- stop_stream(oxfw);
+ struct cmp_connection *conn;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ if (cmp_connection_update(conn) < 0)
+ stop_stream(oxfw, stream);
else
- amdtp_stream_update(&oxfw->rx_stream);
+ amdtp_stream_update(stream);
}
int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
@@ -408,7 +545,10 @@ static int fill_stream_formats(struct snd_oxfw *oxfw,
if (buf == NULL)
return -ENOMEM;
- formats = oxfw->rx_stream_formats;
+ if (dir == AVC_GENERAL_PLUG_DIR_OUT)
+ formats = oxfw->tx_stream_formats;
+ else
+ formats = oxfw->rx_stream_formats;
/* get first entry */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
@@ -481,11 +621,19 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
"fail to get info for isoc/external in/out plugs: %d\n",
err);
goto end;
- } else if (plugs[0] == 0) {
+ } else if ((plugs[0] == 0) && (plugs[1] == 0)) {
err = -ENOSYS;
goto end;
}
+ /* use oPCR[0] if exists */
+ if (plugs[1] > 0) {
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+ oxfw->has_output = true;
+ }
+
/* use iPCR[0] if exists */
if (plugs[0] > 0)
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);