ALSA: firewire-lib: check cycle continuity
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tue, 18 May 2021 13:00:46 +0000 (22:00 +0900)
committerTakashi Iwai <tiwai@suse.de>
Tue, 18 May 2021 16:12:42 +0000 (18:12 +0200)
Within devices supported by drivers in ALSA firewire stack, OXFW-based
devices and Fireface devices are known to skip isochronous cycle for
packet transmission. The former is due to the jumbo payload quirk. The
latter is due to vendor protocol in which empty packet is not
transferred in blocking mode.

Although nothing to do just for handling events of the packet, packet
continuity is necessarily for media clock recovery. This commit checks
whether any cycle is continue or not.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20210518130048.146596-8-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/amdtp-stream.c
sound/firewire/amdtp-stream.h

index 1ff25e6b0c786a709d8c39a860f27815975710ed..78b62a452d562a12e194eebecdf19ae44ee579ef 100644 (file)
@@ -699,6 +699,16 @@ static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend)
        return cycle;
 }
 
+static int compare_ohci_cycle_count(u32 lval, u32 rval)
+{
+       if (lval == rval)
+               return 0;
+       else if (lval < rval && rval - lval < OHCI_SECOND_MODULUS * CYCLES_PER_SECOND / 2)
+               return -1;
+       else
+               return 1;
+}
+
 // Align to actual cycle count for the packet which is going to be scheduled.
 // This module queued the same number of isochronous cycle as the size of queue
 // to kip isochronous cycle, therefore it's OK to just increment the cycle by
@@ -715,6 +725,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
                                     const __be32 *ctx_header,
                                     unsigned int packets)
 {
+       unsigned int next_cycle = s->next_cycle;
        unsigned int dbc = s->data_block_counter;
        unsigned int packet_index = s->packet_index;
        unsigned int queue_size = s->queue_size;
@@ -724,10 +735,31 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
        for (i = 0; i < packets; ++i) {
                struct pkt_desc *desc = descs + i;
                unsigned int cycle;
+               bool lost;
                unsigned int data_blocks;
                unsigned int syt;
 
                cycle = compute_ohci_cycle_count(ctx_header[1]);
+               lost = (next_cycle != cycle);
+               if (lost) {
+                       if (s->flags & CIP_NO_HEADER) {
+                               // Fireface skips transmission just for an isoc cycle corresponding
+                               // to empty packet.
+                               next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+                               lost = (next_cycle != cycle);
+                       } else if (s->flags & CIP_JUMBO_PAYLOAD) {
+                               // OXFW970 skips transmission for several isoc cycles during
+                               // asynchronous transaction.
+                               unsigned int safe_cycle = increment_ohci_cycle_count(next_cycle,
+                                                               IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES);
+                               lost = (compare_ohci_cycle_count(safe_cycle, cycle) > 0);
+                       }
+                       if (lost) {
+                               dev_err(&s->unit->device, "Detect discontinuity of cycle: %d %d\n",
+                                       next_cycle, cycle);
+                               return -EIO;
+                       }
+               }
 
                err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt,
                                          packet_index, i);
@@ -743,12 +775,12 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
                if (!(s->flags & CIP_DBC_IS_END_EVENT))
                        dbc = (dbc + desc->data_blocks) & 0xff;
 
-               ctx_header +=
-                       s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
-
+               next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+               ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
                packet_index = (packet_index + 1) % queue_size;
        }
 
+       s->next_cycle = next_cycle;
        s->data_block_counter = dbc;
 
        return 0;
@@ -1022,6 +1054,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
 
        if (s->direction == AMDTP_IN_STREAM) {
                cycle = compute_ohci_cycle_count(ctx_header[1]);
+               s->next_cycle = cycle;
 
                context->callback.sc = in_stream_callback;
        } else {
index 5f5e4d938a0d27e4e7c7a8359b23a99baa204a7c..58769ca184a2a5ad714210be219617d63248647f 100644 (file)
@@ -171,6 +171,7 @@ struct amdtp_stream {
        bool callbacked;
        wait_queue_head_t callback_wait;
        u32 start_cycle;
+       unsigned int next_cycle;
 
        /* For backends to process data blocks. */
        void *protocol;