Merge tag 'asoc-v5.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-2.6-block.git] / sound / firewire / fireworks / fireworks_stream.c
index 2d30954124272c750fc0b9f655a151299adbd094..e659a0b89ba5b0c784185cc65a049b94dd94b610 100644 (file)
@@ -42,7 +42,6 @@ end:
 static void
 stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
 {
-       amdtp_stream_pcm_abort(stream);
        amdtp_stream_stop(stream);
 
        if (stream == &efw->tx_stream)
@@ -51,54 +50,37 @@ stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
                cmp_connection_break(&efw->in_conn);
 }
 
-static int
-start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
-            unsigned int sampling_rate)
+static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+                       unsigned int rate)
 {
        struct cmp_connection *conn;
-       unsigned int mode, pcm_channels, midi_ports;
        int err;
 
-       err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
-       if (err < 0)
-               goto end;
-       if (stream == &efw->tx_stream) {
+       if (stream == &efw->tx_stream)
                conn = &efw->out_conn;
-               pcm_channels = efw->pcm_capture_channels[mode];
-               midi_ports = efw->midi_out_ports;
-       } else {
+       else
                conn = &efw->in_conn;
-               pcm_channels = efw->pcm_playback_channels[mode];
-               midi_ports = efw->midi_in_ports;
-       }
-
-       err = amdtp_am824_set_parameters(stream, sampling_rate,
-                                        pcm_channels, midi_ports, false);
-       if (err < 0)
-               goto end;
 
-       /*  establish connection via CMP */
-       err = cmp_connection_establish(conn,
-                               amdtp_stream_get_max_payload(stream));
+       // Establish connection via CMP.
+       err = cmp_connection_establish(conn);
        if (err < 0)
-               goto end;
+               return err;
 
-       /* start amdtp stream */
-       err = amdtp_stream_start(stream,
-                                conn->resources.channel,
-                                conn->speed);
+       // Start amdtp stream.
+       err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
        if (err < 0) {
-               stop_stream(efw, stream);
-               goto end;
+               cmp_connection_break(conn);
+               return err;
        }
 
-       /* wait first callback */
+       // Wait first callback.
        if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
-               stop_stream(efw, stream);
-               err = -ETIMEDOUT;
+               amdtp_stream_stop(stream);
+               cmp_connection_break(conn);
+               return -ETIMEDOUT;
        }
-end:
-       return err;
+
+       return 0;
 }
 
 /*
@@ -164,13 +146,13 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
            (efw->firmware_version == 0x5070000 ||
             efw->firmware_version == 0x5070300 ||
             efw->firmware_version == 0x5080000))
-               efw->tx_stream.tx_first_dbc = 0x02;
+               efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
        /* AudioFire9 always reports wrong dbs. */
        if (efw->is_af9)
                efw->tx_stream.flags |= CIP_WRONG_DBS;
        /* Firmware version 5.5 reports fixed interval for dbc. */
        if (efw->firmware_version == 0x5050000)
-               efw->tx_stream.tx_dbc_interval = 8;
+               efw->tx_stream.ctx_data.tx.dbc_interval = 8;
 
        err = init_stream(efw, &efw->rx_stream);
        if (err < 0) {
@@ -188,75 +170,135 @@ end:
        return err;
 }
 
-int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
+static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
+                         unsigned int rate, unsigned int mode)
 {
-       unsigned int curr_rate;
-       int err = 0;
+       unsigned int pcm_channels;
+       unsigned int midi_ports;
+       struct cmp_connection *conn;
+       int err;
 
-       /* Need no substreams */
-       if (efw->playback_substreams == 0 && efw->capture_substreams  == 0)
-               goto end;
+       if (stream == &efw->tx_stream) {
+               pcm_channels = efw->pcm_capture_channels[mode];
+               midi_ports = efw->midi_out_ports;
+               conn = &efw->out_conn;
+       } else {
+               pcm_channels = efw->pcm_playback_channels[mode];
+               midi_ports = efw->midi_in_ports;
+               conn = &efw->in_conn;
+       }
 
-       /*
-        * Considering JACK/FFADO streaming:
-        * TODO: This can be removed hwdep functionality becomes popular.
-        */
-       err = check_connection_used_by_others(efw, &efw->rx_stream);
+       err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
+                                        midi_ports, false);
        if (err < 0)
-               goto end;
+               return err;
 
-       /* packet queueing error */
-       if (amdtp_streaming_error(&efw->tx_stream))
-               stop_stream(efw, &efw->tx_stream);
-       if (amdtp_streaming_error(&efw->rx_stream))
-               stop_stream(efw, &efw->rx_stream);
+       return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
 
-       /* stop streams if rate is different */
+       // Considering JACK/FFADO streaming:
+       // TODO: This can be removed hwdep functionality becomes popular.
+       err = check_connection_used_by_others(efw, &efw->rx_stream);
+       if (err < 0)
+               return err;
+
+       // stop streams if rate is different.
        err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
        if (err < 0)
-               goto end;
+               return err;
        if (rate == 0)
                rate = curr_rate;
        if (rate != curr_rate) {
                stop_stream(efw, &efw->tx_stream);
                stop_stream(efw, &efw->rx_stream);
+
+               cmp_connection_release(&efw->out_conn);
+               cmp_connection_release(&efw->in_conn);
        }
 
-       /* master should be always running */
-       if (!amdtp_stream_running(&efw->rx_stream)) {
+       if (efw->substreams_counter == 0 || rate != curr_rate) {
+               unsigned int mode;
+
                err = snd_efw_command_set_sampling_rate(efw, rate);
                if (err < 0)
-                       goto end;
+                       return err;
+
+               err = snd_efw_get_multiplier_mode(rate, &mode);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(efw, &efw->tx_stream, rate, mode);
+               if (err < 0)
+                       return err;
 
+               err = keep_resources(efw, &efw->rx_stream, rate, mode);
+               if (err < 0) {
+                       cmp_connection_release(&efw->in_conn);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw)
+{
+       unsigned int rate;
+       int err = 0;
+
+       // Need no substreams.
+       if (efw->substreams_counter == 0)
+               return -EIO;
+
+       err = snd_efw_command_get_sampling_rate(efw, &rate);
+       if (err < 0)
+               return err;
+
+       if (amdtp_streaming_error(&efw->rx_stream) ||
+           amdtp_streaming_error(&efw->tx_stream)) {
+               stop_stream(efw, &efw->rx_stream);
+               stop_stream(efw, &efw->tx_stream);
+       }
+
+       /* master should be always running */
+       if (!amdtp_stream_running(&efw->rx_stream)) {
                err = start_stream(efw, &efw->rx_stream, rate);
                if (err < 0) {
                        dev_err(&efw->unit->device,
                                "fail to start AMDTP master stream:%d\n", err);
-                       goto end;
+                       goto error;
                }
        }
 
-       /* start slave if needed */
-       if (efw->capture_substreams > 0 &&
-           !amdtp_stream_running(&efw->tx_stream)) {
+       if (!amdtp_stream_running(&efw->tx_stream)) {
                err = start_stream(efw, &efw->tx_stream, rate);
                if (err < 0) {
                        dev_err(&efw->unit->device,
                                "fail to start AMDTP slave stream:%d\n", err);
-                       stop_stream(efw, &efw->rx_stream);
+                       goto error;
                }
        }
-end:
+
+       return 0;
+error:
+       stop_stream(efw, &efw->rx_stream);
+       stop_stream(efw, &efw->tx_stream);
        return err;
 }
 
 void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 {
-       if (efw->capture_substreams == 0) {
+       if (efw->substreams_counter == 0) {
                stop_stream(efw, &efw->tx_stream);
+               stop_stream(efw, &efw->rx_stream);
 
-               if (efw->playback_substreams == 0)
-                       stop_stream(efw, &efw->rx_stream);
+               cmp_connection_release(&efw->out_conn);
+               cmp_connection_release(&efw->in_conn);
        }
 }