summaryrefslogtreecommitdiffstats
path: root/sound/firewire/oxfw/oxfw-stream.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-09 09:59:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-09 09:59:43 -0700
commit4cdd5f9186bbe80306e76f11da7ecb0b9720433c (patch)
tree23c2f39933cd8253a65385eab00405beaf602f01 /sound/firewire/oxfw/oxfw-stream.c
parent2d41ef5432b76ae90dc0db93026f1d981f874ec4 (diff)
parent0dcb4efb1095d0a1f5f681c2b94e98b009cc5d77 (diff)
downloadtalos-op-linux-4cdd5f9186bbe80306e76f11da7ecb0b9720433c.tar.gz
talos-op-linux-4cdd5f9186bbe80306e76f11da7ecb0b9720433c.zip
Merge tag 'sound-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "Many updates in this development cycle are found in ASoC where it got a wide range of changes for the continued refactoring. Some highlights are below. ASoC: - Continued refactoring work by Morimoto-san toward the full componentization; the changes are seen allover the places - Support for force disconnecting muxes in DAPM - Continued development of ASoC Intel SOF stuff - New drivers for Cirrus Logic CS47L35, CS47L85 and CS47L90, Conexant CX2072X, Realtek RT1011 and RT1308 HD-audio: - More fixes and adjustments for ASoC SOF HD-audio - Fix for resume problem on some Realtek codecs USB-audio: - A few fixes for the issues reported by syzbot USB fuzzer - Fix for UAC2 extension unit parser - Quirks for Line6 Helix, Emgaic Unitor 8 FireWire: - Lots of code refactoring and fixes in most of its components" * tag 'sound-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (626 commits) ALSA: firewire-lib: code refactoring for local variables ALSA: firewire-lib: code refactoring for post operation to data block counter ALSA: firewire-lib: code refactoring for error path of parser for CIP header ALSA: firewire-lib: fix different data block counter between probed event and transferred isochronous packet ALSA: firewire-lib: fix initial value of data block count for IR context without CIP_DBC_IS_END_EVENT ALSA: firewire-lib/fireface: fix initial value of data block counter for IR context with CIP_NO_HEADER ALSA: firewire-lib: fix invalid length of rx packet payload for tracepoint events ALSA: usb-audio: fix Line6 Helix audio format rates firewire-motu: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: firewire-digi00x: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: dice: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: oxfw: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: fireworks: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: bebob: fix wrong reference count for stream functionality at error path of rawmidi interface ASoC: SOF: Intel: implement runtime idle for CNL/APL ASoC: SOF: add runtime idle callback ASoC: hdac_hdmi: report codec link up/down status to bus ASoC: SOF: debug: fix possible memory leak in sof_dfsentry_write() ASoC: sunxi: sun50i-codec-analog: Add earpiece ASoC: rt5665: remove redundant assignment to variable idx ...
Diffstat (limited to 'sound/firewire/oxfw/oxfw-stream.c')
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c351
1 files changed, 203 insertions, 148 deletions
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 5ffedb0ade3f..74c972d25c66 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -100,85 +100,34 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
return 0;
}
-static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
- amdtp_stream_pcm_abort(stream);
- amdtp_stream_stop(stream);
-
- 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, 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;
int err;
- if (stream == &oxfw->rx_stream) {
- formats = oxfw->rx_stream_formats;
+ if (stream == &oxfw->rx_stream)
conn = &oxfw->in_conn;
- } else {
- formats = oxfw->tx_stream_formats;
+ else
conn = &oxfw->out_conn;
- }
-
- /* Get stream format */
- for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
- if (formats[i] == NULL)
- break;
-
- err = snd_oxfw_stream_parse_format(formats[i], &formation);
- if (err < 0)
- goto end;
- if (rate != formation.rate)
- continue;
- if (pcm_channels == 0 || pcm_channels == formation.pcm)
- break;
- }
- if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
- err = -EINVAL;
- goto end;
- }
-
- pcm_channels = formation.pcm;
- midi_ports = formation.midi * 8;
-
- /* The stream should have one pcm channels at least */
- if (pcm_channels == 0) {
- err = -EINVAL;
- goto end;
- }
- err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
- false);
- if (err < 0)
- goto end;
- err = cmp_connection_establish(conn,
- amdtp_stream_get_max_payload(stream));
+ err = cmp_connection_establish(conn);
if (err < 0)
- goto end;
+ return err;
- err = amdtp_stream_start(stream,
- conn->resources.channel,
- conn->speed);
+ err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
if (err < 0) {
cmp_connection_break(conn);
- goto end;
+ return err;
}
- /* Wait first packet */
+ // Wait first packet.
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
- stop_stream(oxfw, stream);
- err = -ETIMEDOUT;
+ amdtp_stream_stop(stream);
+ cmp_connection_break(conn);
+ return -ETIMEDOUT;
}
-end:
- return err;
+
+ return 0;
}
static int check_connection_used_by_others(struct snd_oxfw *oxfw,
@@ -205,8 +154,7 @@ static int check_connection_used_by_others(struct snd_oxfw *oxfw,
return err;
}
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
enum cmp_direction c_dir;
@@ -225,13 +173,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
if (err < 0)
- goto end;
+ return err;
err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
if (err < 0) {
- amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
- goto end;
+ return err;
}
/*
@@ -245,115 +192,195 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
if (oxfw->wrong_dbs)
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
}
-end:
- return err;
+
+ return 0;
}
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream,
- unsigned int rate, unsigned int pcm_channels)
+static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
- struct amdtp_stream *opposite;
- struct snd_oxfw_stream_formation formation;
enum avc_general_plug_dir dir;
- unsigned int substreams, opposite_substreams;
- int err = 0;
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ struct cmp_connection *conn;
+ int i;
+ int err;
- if (stream == &oxfw->tx_stream) {
- substreams = oxfw->capture_substreams;
- opposite = &oxfw->rx_stream;
- opposite_substreams = oxfw->playback_substreams;
- dir = AVC_GENERAL_PLUG_DIR_OUT;
+ if (stream == &oxfw->rx_stream) {
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ formats = oxfw->rx_stream_formats;
+ conn = &oxfw->in_conn;
} else {
- substreams = oxfw->playback_substreams;
- opposite_substreams = oxfw->capture_substreams;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ formats = oxfw->tx_stream_formats;
+ conn = &oxfw->out_conn;
+ }
- if (oxfw->has_output)
- opposite = &oxfw->rx_stream;
- else
- opposite = NULL;
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ return err;
- dir = AVC_GENERAL_PLUG_DIR_IN;
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ struct snd_oxfw_stream_formation fmt;
+
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &fmt);
+ if (err < 0)
+ return err;
+
+ if (fmt.rate == formation.rate && fmt.pcm == formation.pcm &&
+ fmt.midi == formation.midi)
+ break;
}
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EINVAL;
- if (substreams == 0)
- goto end;
+ // The stream should have one pcm channels at least.
+ if (formation.pcm == 0)
+ return -EINVAL;
- /*
- * Considering JACK/FFADO streaming:
- * TODO: This can be removed hwdep functionality becomes popular.
- */
- err = check_connection_used_by_others(oxfw, stream);
+ err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
+ formation.midi * 8, false);
if (err < 0)
- goto end;
+ return err;
- /* packet queueing error */
- if (amdtp_streaming_error(stream))
- stop_stream(oxfw, stream);
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+ if (oxfw->has_output) {
+ err = check_connection_used_by_others(oxfw, &oxfw->tx_stream);
+ if (err < 0)
+ return err;
+ }
+
+ if (stream == &oxfw->tx_stream)
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ else
+ dir = AVC_GENERAL_PLUG_DIR_IN;
err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
if (err < 0)
- goto end;
- if (rate == 0)
+ return err;
+ if (rate == 0) {
rate = formation.rate;
- if (pcm_channels == 0)
pcm_channels = formation.pcm;
+ }
+ if (formation.rate != rate || formation.pcm != pcm_channels) {
+ amdtp_stream_stop(&oxfw->rx_stream);
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
- if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
- if (opposite != NULL) {
- err = check_connection_used_by_others(oxfw, opposite);
- if (err < 0)
- goto end;
- stop_stream(oxfw, opposite);
+ if (oxfw->has_output) {
+ amdtp_stream_stop(&oxfw->tx_stream);
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
}
- stop_stream(oxfw, stream);
+ }
+ if (oxfw->substreams_count == 0 ||
+ formation.rate != rate || formation.pcm != 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;
+ return err;
}
- /* Start opposite stream if needed. */
- if (opposite && !amdtp_stream_running(opposite) &&
- (opposite_substreams > 0)) {
- err = start_stream(oxfw, opposite, rate, 0);
+ err = keep_resources(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+
+ if (oxfw->has_output) {
+ err = keep_resources(oxfw, &oxfw->tx_stream);
if (err < 0) {
- dev_err(&oxfw->unit->device,
- "fail to restart stream: %d\n", err);
- goto end;
+ cmp_connection_release(&oxfw->in_conn);
+ return err;
}
}
}
- /* Start requested stream. */
- if (!amdtp_stream_running(stream)) {
- err = start_stream(oxfw, stream, rate, pcm_channels);
- if (err < 0)
+ return 0;
+}
+
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ if (oxfw->substreams_count == 0)
+ return -EIO;
+
+ if (amdtp_streaming_error(&oxfw->rx_stream) ||
+ amdtp_streaming_error(&oxfw->tx_stream)) {
+ amdtp_stream_stop(&oxfw->rx_stream);
+ cmp_connection_break(&oxfw->in_conn);
+
+ if (oxfw->has_output) {
+ amdtp_stream_stop(&oxfw->tx_stream);
+ cmp_connection_break(&oxfw->out_conn);
+ }
+ }
+
+ if (!amdtp_stream_running(&oxfw->rx_stream)) {
+ err = start_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0) {
dev_err(&oxfw->unit->device,
- "fail to start stream: %d\n", err);
+ "fail to start rx stream: %d\n", err);
+ goto error;
+ }
+ }
+
+ if (oxfw->has_output) {
+ if (!amdtp_stream_running(&oxfw->tx_stream)) {
+ err = start_stream(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to start tx stream: %d\n", err);
+ goto error;
+ }
+ }
+ }
+
+ return 0;
+error:
+ amdtp_stream_stop(&oxfw->rx_stream);
+ cmp_connection_break(&oxfw->in_conn);
+ if (oxfw->has_output) {
+ amdtp_stream_stop(&oxfw->tx_stream);
+ cmp_connection_break(&oxfw->out_conn);
}
-end:
return err;
}
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
{
- if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
- ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
- return;
+ if (oxfw->substreams_count == 0) {
+ amdtp_stream_stop(&oxfw->rx_stream);
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
- stop_stream(oxfw, stream);
+ if (oxfw->has_output) {
+ amdtp_stream_stop(&oxfw->tx_stream);
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
+ }
+ }
}
-/*
- * This function should be called before starting the stream or after stopping
- * the streams.
- */
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
@@ -366,20 +393,48 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
cmp_connection_destroy(conn);
}
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
- struct amdtp_stream *stream)
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
{
- struct cmp_connection *conn;
+ int err;
- if (stream == &oxfw->tx_stream)
- conn = &oxfw->out_conn;
- else
- conn = &oxfw->in_conn;
+ err = init_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
- if (cmp_connection_update(conn) < 0)
- stop_stream(oxfw, stream);
- else
- amdtp_stream_update(stream);
+ if (oxfw->has_output) {
+ err = init_stream(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ destroy_stream(oxfw, &oxfw->rx_stream);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+// This function should be called before starting the stream or after stopping
+// the streams.
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
+{
+ destroy_stream(oxfw, &oxfw->rx_stream);
+
+ if (oxfw->has_output)
+ destroy_stream(oxfw, &oxfw->tx_stream);
+}
+
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
+{
+ amdtp_stream_stop(&oxfw->rx_stream);
+ cmp_connection_break(&oxfw->in_conn);
+
+ amdtp_stream_pcm_abort(&oxfw->rx_stream);
+
+ if (oxfw->has_output) {
+ amdtp_stream_stop(&oxfw->tx_stream);
+ cmp_connection_break(&oxfw->out_conn);
+
+ amdtp_stream_pcm_abort(&oxfw->tx_stream);
+ }
}
int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
OpenPOWER on IntegriCloud