Merge tag 'asoc-v5.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-2.6-block.git] / sound / firewire / motu / motu-stream.c
index 81f7edc560d0490a5d9ce93d632c0757b535d7bf..2bbb335e8de199f3ba0ba00be859bc6ceb8a07aa 100644 (file)
 #define  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS        0x00000040
 #define  TX_PACKET_TRANSMISSION_SPEED_MASK     0x0000000f
 
-static int start_both_streams(struct snd_motu *motu, unsigned int rate)
+static int keep_resources(struct snd_motu *motu, unsigned int rate,
+                         struct amdtp_stream *stream)
 {
+       struct fw_iso_resources *resources;
+       struct snd_motu_packet_format *packet_format;
        unsigned int midi_ports = 0;
-       __be32 reg;
-       u32 data;
        int err;
 
-       if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
-           (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
-               midi_ports = 1;
+       if (stream == &motu->rx_stream) {
+               resources = &motu->rx_resources;
+               packet_format = &motu->rx_packet_formats;
 
-       /* Set packet formation to our packet streaming engine. */
-       err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
-                                       &motu->rx_packet_formats);
-       if (err < 0)
-               return err;
+               if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
+                   (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
+                       midi_ports = 1;
+       } else {
+               resources = &motu->tx_resources;
+               packet_format = &motu->tx_packet_formats;
 
-       if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
-           (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
-               midi_ports = 1;
-       else
-               midi_ports = 0;
+               if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
+                   (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
+                       midi_ports = 1;
+       }
 
-       err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
-                                       &motu->tx_packet_formats);
+       err = amdtp_motu_set_parameters(stream, rate, midi_ports,
+                                       packet_format);
        if (err < 0)
                return err;
 
-       /* Get isochronous resources on the bus. */
-       err = fw_iso_resources_allocate(&motu->rx_resources,
-                               amdtp_stream_get_max_payload(&motu->rx_stream),
+       return fw_iso_resources_allocate(resources,
+                               amdtp_stream_get_max_payload(stream),
                                fw_parent_device(motu->unit)->max_speed);
-       if (err < 0)
-               return err;
+}
 
-       err = fw_iso_resources_allocate(&motu->tx_resources,
-                               amdtp_stream_get_max_payload(&motu->tx_stream),
-                               fw_parent_device(motu->unit)->max_speed);
-       if (err < 0)
-               return err;
+static int begin_session(struct snd_motu *motu)
+{
+       __be32 reg;
+       u32 data;
+       int err;
 
-       /* Configure the unit to start isochronous communication. */
+       // Configure the unit to start isochronous communication.
        err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
                                        sizeof(reg));
        if (err < 0)
@@ -83,7 +82,7 @@ static int start_both_streams(struct snd_motu *motu, unsigned int rate)
                                          sizeof(reg));
 }
 
-static void stop_both_streams(struct snd_motu *motu)
+static void finish_session(struct snd_motu *motu)
 {
        __be32 reg;
        u32 data;
@@ -93,6 +92,9 @@ static void stop_both_streams(struct snd_motu *motu)
        if (err < 0)
                return;
 
+       amdtp_stream_stop(&motu->tx_stream);
+       amdtp_stream_stop(&motu->rx_stream);
+
        err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
                                        sizeof(reg));
        if (err < 0)
@@ -105,9 +107,6 @@ static void stop_both_streams(struct snd_motu *motu)
        reg = cpu_to_be32(data);
        snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
                                   sizeof(reg));
-
-       fw_iso_resources_free(&motu->tx_resources);
-       fw_iso_resources_free(&motu->rx_resources);
 }
 
 static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
@@ -125,28 +124,12 @@ static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
        if (err < 0)
                return err;
 
-       if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
-               amdtp_stream_stop(stream);
-               fw_iso_resources_free(resources);
+       if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT))
                return -ETIMEDOUT;
-       }
 
        return 0;
 }
 
-static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
-{
-       struct fw_iso_resources *resources;
-
-       if (stream == &motu->rx_stream)
-               resources = &motu->rx_resources;
-       else
-               resources = &motu->tx_resources;
-
-       amdtp_stream_stop(stream);
-       fw_iso_resources_free(resources);
-}
-
 int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
 {
        int err;
@@ -174,6 +157,48 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
        return 0;
 }
 
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
+
+       err = motu->spec->protocol->get_clock_rate(motu, &curr_rate);
+       if (err < 0)
+               return err;
+       if (rate == 0)
+               rate = curr_rate;
+
+       if (motu->substreams_counter == 0 || curr_rate != rate) {
+               finish_session(motu);
+
+               fw_iso_resources_free(&motu->tx_resources);
+               fw_iso_resources_free(&motu->rx_resources);
+
+               err = motu->spec->protocol->set_clock_rate(motu, rate);
+               if (err < 0) {
+                       dev_err(&motu->unit->device,
+                               "fail to set sampling rate: %d\n", err);
+                       return err;
+               }
+
+               err = snd_motu_stream_cache_packet_formats(motu);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(motu, rate, &motu->tx_stream);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(motu, rate, &motu->rx_stream);
+               if (err < 0) {
+                       fw_iso_resources_free(&motu->tx_resources);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
 static int ensure_packet_formats(struct snd_motu *motu)
 {
        __be32 reg;
@@ -200,55 +225,34 @@ static int ensure_packet_formats(struct snd_motu *motu)
                                          sizeof(reg));
 }
 
-int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
+int snd_motu_stream_start_duplex(struct snd_motu *motu)
 {
-       const struct snd_motu_protocol *protocol = motu->spec->protocol;
-       unsigned int curr_rate;
+       unsigned int generation = motu->rx_resources.generation;
        int err = 0;
 
-       if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
+       if (motu->substreams_counter == 0)
                return 0;
 
-       /* Some packet queueing errors. */
        if (amdtp_streaming_error(&motu->rx_stream) ||
-           amdtp_streaming_error(&motu->tx_stream)) {
-               amdtp_stream_stop(&motu->rx_stream);
-               amdtp_stream_stop(&motu->tx_stream);
-               stop_both_streams(motu);
-       }
+           amdtp_streaming_error(&motu->tx_stream))
+               finish_session(motu);
 
-       err = snd_motu_stream_cache_packet_formats(motu);
-       if (err < 0)
-               return err;
+       if (generation != fw_parent_device(motu->unit)->card->generation) {
+               err = fw_iso_resources_update(&motu->rx_resources);
+               if (err < 0)
+                       return err;
 
-       /* Stop stream if rate is different. */
-       err = protocol->get_clock_rate(motu, &curr_rate);
-       if (err < 0) {
-               dev_err(&motu->unit->device,
-                       "fail to get sampling rate: %d\n", err);
-               return err;
-       }
-       if (rate == 0)
-               rate = curr_rate;
-       if (rate != curr_rate) {
-               amdtp_stream_stop(&motu->rx_stream);
-               amdtp_stream_stop(&motu->tx_stream);
-               stop_both_streams(motu);
+               err = fw_iso_resources_update(&motu->tx_resources);
+               if (err < 0)
+                       return err;
        }
 
        if (!amdtp_stream_running(&motu->rx_stream)) {
-               err = protocol->set_clock_rate(motu, rate);
-               if (err < 0) {
-                       dev_err(&motu->unit->device,
-                               "fail to set sampling rate: %d\n", err);
-                       return err;
-               }
-
                err = ensure_packet_formats(motu);
                if (err < 0)
                        return err;
 
-               err = start_both_streams(motu, rate);
+               err = begin_session(motu);
                if (err < 0) {
                        dev_err(&motu->unit->device,
                                "fail to start isochronous comm: %d\n", err);
@@ -262,7 +266,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
                        goto stop_streams;
                }
 
-               err = protocol->switch_fetching_mode(motu, true);
+               err = motu->spec->protocol->switch_fetching_mode(motu, true);
                if (err < 0) {
                        dev_err(&motu->unit->device,
                                "fail to enable frame fetching: %d\n", err);
@@ -270,13 +274,11 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
                }
        }
 
-       if (!amdtp_stream_running(&motu->tx_stream) &&
-           motu->capture_substreams > 0) {
+       if (!amdtp_stream_running(&motu->tx_stream)) {
                err = start_isoc_ctx(motu, &motu->tx_stream);
                if (err < 0) {
                        dev_err(&motu->unit->device,
                                "fail to start IR context: %d", err);
-                       amdtp_stream_stop(&motu->rx_stream);
                        goto stop_streams;
                }
        }
@@ -284,21 +286,17 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
        return 0;
 
 stop_streams:
-       stop_both_streams(motu);
+       finish_session(motu);
        return err;
 }
 
 void snd_motu_stream_stop_duplex(struct snd_motu *motu)
 {
-       if (motu->capture_substreams == 0) {
-               if (amdtp_stream_running(&motu->tx_stream))
-                       stop_isoc_ctx(motu, &motu->tx_stream);
-
-               if (motu->playback_substreams == 0) {
-                       if (amdtp_stream_running(&motu->rx_stream))
-                               stop_isoc_ctx(motu, &motu->rx_stream);
-                       stop_both_streams(motu);
-               }
+       if (motu->substreams_counter == 0) {
+               finish_session(motu);
+
+               fw_iso_resources_free(&motu->tx_resources);
+               fw_iso_resources_free(&motu->rx_resources);
        }
 }
 
@@ -371,8 +369,7 @@ void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
        destroy_stream(motu, AMDTP_IN_STREAM);
        destroy_stream(motu, AMDTP_OUT_STREAM);
 
-       motu->playback_substreams = 0;
-       motu->capture_substreams = 0;
+       motu->substreams_counter = 0;
 }
 
 static void motu_lock_changed(struct snd_motu *motu)