Merge tag 'asoc-v5.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-2.6-block.git] / sound / firewire / oxfw / oxfw-stream.c
index 5ffedb0ade3f8e93713ff44fc07ab9ced3b21f4d..74c972d25c6642934e7cefa77588ac74be980123 100644 (file)
@@ -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,