ALSA: fireface: use the same size of period for PCM substreams in AMDTP streams
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Mon, 7 Oct 2019 11:05:32 +0000 (20:05 +0900)
committerTakashi Iwai <tiwai@suse.de>
Thu, 17 Oct 2019 10:02:47 +0000 (12:02 +0200)
In current implementation, when opening a PCM substream, it's needed to
check whether the opposite PCM substream runs. This is to assign
effectual constraints (e.g. sampling rate) to opened PCM substream.

The number of PCM substreams on AMDTP streams in domain is recorded in
own structure. Usage of this count is an alternative of the above check.
This is better because the count is incremented in pcm.hw_params earlier
than pcm.trigger.

This commit replaces the check with the substream count and the value for
the size of PCM period. Unlike the other drivers in ALSA firewire stack,
no MIDI substream is multiplexed into AMDTP stream.

I note that Fireface AMDTP protocol has a quirk that tx stream includes
blank isochronous cycle. The packet for blank cycle is equivalent to
empty or NODATA packet in IEC 61883-6, thus the protocol is similar to
blocking transmission method of IEC 61883-6. On the other hand, rx
stream adopts non-blocking transmission method. Although the difference
of transmission method between tx/rx streams precisely brings different
timing for a certain amount of events due to their different calculation
for data blocks per packet, it's possible to approximate enough amount
of events mostly has the same timing. Actually current ALSA IEC 61883-1/6
engine uses large amount of data blocks for each hardware IRQ
(=16 packets).

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20191007110532.30270-18-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/fireface/ff-pcm.c

index 4f2208202494af15d41bf5d430e821c98f1ea684..415bc9ccd1c70d63c48138beba897a95748b749e 100644 (file)
@@ -139,6 +139,7 @@ static int pcm_init_hw_params(struct snd_ff *ff,
 static int pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_ff *ff = substream->private_data;
+       struct amdtp_domain *d = &ff->domain;
        unsigned int rate;
        enum snd_ff_clock_src src;
        int i, err;
@@ -155,16 +156,21 @@ static int pcm_open(struct snd_pcm_substream *substream)
        if (err < 0)
                goto release_lock;
 
+       mutex_lock(&ff->mutex);
+
+       // When source of clock is not internal or any stream is reserved for
+       // transmission of PCM frames, the available sampling rate is limited
+       // at current one.
        if (src != SND_FF_CLOCK_SRC_INTERNAL) {
                for (i = 0; i < CIP_SFC_COUNT; ++i) {
                        if (amdtp_rate_table[i] == rate)
                                break;
                }
-               /*
-                * The unit is configured at sampling frequency which packet
-                * streaming engine can't support.
-                */
+
+               // The unit is configured at sampling frequency which packet
+               // streaming engine can't support.
                if (i >= CIP_SFC_COUNT) {
+                       mutex_unlock(&ff->mutex);
                        err = -EIO;
                        goto release_lock;
                }
@@ -172,14 +178,25 @@ static int pcm_open(struct snd_pcm_substream *substream)
                substream->runtime->hw.rate_min = rate;
                substream->runtime->hw.rate_max = rate;
        } else {
-               if (amdtp_stream_pcm_running(&ff->rx_stream) ||
-                   amdtp_stream_pcm_running(&ff->tx_stream)) {
+               if (ff->substreams_counter > 0) {
+                       unsigned int frames_per_period = d->events_per_period;
+
                        rate = amdtp_rate_table[ff->rx_stream.sfc];
                        substream->runtime->hw.rate_min = rate;
                        substream->runtime->hw.rate_max = rate;
+
+                       err = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                       SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                       frames_per_period, frames_per_period);
+                       if (err < 0) {
+                               mutex_unlock(&ff->mutex);
+                               goto release_lock;
+                       }
                }
        }
 
+       mutex_unlock(&ff->mutex);
+
        snd_pcm_set_sync(substream);
 
        return 0;